This commit is contained in:
Tobias Reisinger 2020-03-05 21:46:08 +01:00
commit 9b70aa22b6
13 changed files with 711 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
locale/*/LC_MESSAGES/rhythm.mo
locale/*/LC_MESSAGES/rhythm.po~

16
audio.php Normal file
View file

@ -0,0 +1,16 @@
<?php
const OUTPUT_FOLDER = "./tmp/";
header("Content-Type: audio/ogg");
if(isset($_GET["file_name"]) && is_string($_GET["file_name"]))
{
system("timidity " . OUTPUT_FOLDER . $_GET["file_name"] . ".midi -Ov -o -");
}
else
{
http_response_code(400);
echo("Missing Parameters");
}
?>

9
generate_locale.sh Executable file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env sh
xgettext --add-comments templates/*.php -o locale/rhythm.pot
msgmerge --update locale/de/LC_MESSAGES/rhythm.po locale/rhythm.pot
msgmerge --update locale/en/LC_MESSAGES/rhythm.po locale/rhythm.pot
msgfmt locale/de/LC_MESSAGES/rhythm.po --output-file=locale/de/LC_MESSAGES/rhythm.mo
msgfmt locale/en/LC_MESSAGES/rhythm.po --output-file=locale/en/LC_MESSAGES/rhythm.mo

111
index.php Normal file
View file

@ -0,0 +1,111 @@
<?php
require("./lib//config.php");
require("./lib//helpers.php");
require("./lib//validation.php");
session_start();
setup_locale();
validate_get_parameter();
$id = $_GET["id"];
$tempo = $_GET["tempo"];
$time = $_GET["time"];
$bars = intval($_GET["bars"]);
$dynamic_beat = $_GET["dynamic_beat"];
$dynamic_rhythm = $_GET["dynamic_rhythm"];
$notes_final = [];
$notes_modifiers = [];
$bars_count = 0;
$rhythm_length = 0;
$time_explode = explode("/", $time, 2);
$rhythm_length_max = MAX_FRACTION * (intval($time_explode[0]) / intval($time_explode[1]));
$id_hashed = md5($id);
do {
$notes_input = [];
for($i = 0; $i < strlen($id_hashed); $i+=2)
{
$notes_input[] = strval(pow(2, (ord(substr($id_hashed, $i)) % intval(log(MAX_FRACTION, 2))) + 1));
$notes_modifiers[] = ord(substr($id_hashed, $i + 1));
}
for($i = 0; $i < count($notes_input); $i++)
{
if($bars_count == $bars)
{
continue;
}
$note_val = intval($notes_input[$i]);
if(($note_val == 0) || ($note_val > MAX_FRACTION) || (!is_valid_note($note_val)))
{
continue;
}
if($rhythm_length + (MAX_FRACTION / $note_val) > $rhythm_length_max)
{
continue;
}
$rhythm_length += MAX_FRACTION / $note_val;
if($notes_modifiers[$i] & 0b0111)
{
$notes_final[] = PERCUSSION_CLAP . $note_val;
}
else
{
$notes_final[] = PERCUSSION_REST . $note_val;
}
if($rhythm_length == $rhythm_length_max)
{
$notes_final[] = "|";
$rhythm_length = 0;
$bars_count++;
}
}
$id_hashed = md5($id_hashed);
} while($bars_count < $bars);
$notes_final = implode(" ", $notes_final);
$rest_padding = array_fill(0, $time_explode[0], PERCUSSION_REST . $time_explode[1]);
$rest_padding[0] = $rest_padding[0] . "\\$dynamic_rhythm";
$rest_padding = implode(" ", $rest_padding);
$beats_one_bar = array_fill(1, $time_explode[0] - 1, PERCUSSION_BEAT . $time_explode[1]);
array_unshift($beats_one_bar, PERCUSSION_BEAT_ACCENT . $time_explode[1] . "\\$dynamic_beat");
array_push($beats_one_bar, "|");
$beats = implode(" ", array_fill(0, $bars_count + 2, implode(" ", $beats_one_bar)));
$file_content = sprintf(
LILYPOND_FORMAT,
$time,
$tempo,
$notes_final,
$rest_padding,
$beats
);
$file_name = md5($file_content);
file_put_contents( OUTPUT_FOLDER . $file_name . ".ly", $file_content);
exec(sprintf("lilypond --png -o %1\$s %1\$s$file_name.ly && convert %1\$s$file_name.png -trim %1\$s$file_name.png", OUTPUT_FOLDER));
echo(render(
"./templates/index.tpl.php",
$locale,
array(
"id" => $id,
"time" => $time,
"tempo" => $tempo,
"bars" => $bars,
"file_name" => $file_name,
"dynamic_beat" => $dynamic_beat,
"dynamic_rhythm" => $dynamic_rhythm,
)
));
?>

53
lib/config.php Normal file
View file

@ -0,0 +1,53 @@
<?php
const MAX_FRACTION = 8;
const PERCUSSION_REST = "r";
const PERCUSSION_CLAP = "hc";
const PERCUSSION_BEAT = "ss";
const PERCUSSION_BEAT_ACCENT = "sn";
const OUTPUT_FOLDER = "./tmp/";
const LILYPOND_FORMAT = '\\version "2.20.0"
\\header {
tagline = ""
}
main_source = \\drummode {
\\numericTimeSignature
\\time %1$s
\\tempo %2$s
%3$s
}
main_padded = \\drummode {
%4$s | \\main_source | %4$s
}
main = \\drummode {
\\main_source
}
beat = \\drummode {
\\numericTimeSignature
\\time %1$s
\\tempo %2$s
%5$s
}
\\score {
\\new DrumStaff <<
\\new DrumVoice { \\voiceOne \\main_padded }
\\new DrumVoice { \\voiceTwo \\beat }
>>
\\midi {
\\tempo %2$s
}
}
\\score {
\\new DrumStaff \with {
drumStyleTable = #percussion-style
\override StaffSymbol.line-count = #1
} <<
\\new DrumVoice { \\voiceOne \\main }
>>
\\layout { }
}
\\paper {
}';
?>

37
lib/helpers.php Normal file
View file

@ -0,0 +1,37 @@
<?php
function remove_whitespace($target)
{
return str_replace(" ", "", $target);
}
function render($template, $locale, $param){
ob_start();
extract($param, EXTR_SKIP);
include($template);
$ret = ob_get_contents();
ob_end_clean();
return $ret;
}
function setup_locale()
{
$locale = "de_DE.UTF-8";
if (isset($_SESSION["locale"]))
{
$locale = $_SESSION["locale"];
}
putenv("LC_ALL=$locale");
setlocale(LC_ALL, $locale);
$domain = "rhythm";
bindtextdomain($domain, "./locale");
bind_textdomain_codeset($domain, 'UTF-8');
textdomain($domain);
}
?>

119
lib/validation.php Normal file
View file

@ -0,0 +1,119 @@
<?php
// https://stackoverflow.com/a/600306/9123061
function is_valid_note($x)
{
return ($x & ($x - 1)) == 0;
}
function is_valid_time($check_time)
{
if(!is_string($check_time))
{
return false;
}
$check_time = remove_whitespace($check_time);
$check_time_explode = explode("/", $check_time, 2);
$check_time_explode[0] = intval($check_time_explode[0]);
$check_time_explode[1] = intval($check_time_explode[1]);
if(count($check_time_explode) != 2 || $check_time_explode[0] < 1 || $check_time_explode[1] < 1)
{
return false;
}
if(!is_valid_note($check_time_explode[1]))
{
return false;
}
$ratio = $check_time_explode[0] / $check_time_explode[1];
$new_rhythm_length = intval(MAX_FRACTION * $ratio);
if(!is_int($new_rhythm_length) || $new_rhythm_length == 0)
{
return false;
}
return true;
}
function is_valid_tempo($check_tempo)
{
if(!is_string($check_tempo))
{
return false;
}
$check_tempo = remove_whitespace($check_tempo);
$check_tempo_explode = explode("=", $check_tempo, 2);
$check_tempo_explode[0] = intval($check_tempo_explode[0]);
$check_tempo_explode[1] = intval($check_tempo_explode[1]);
if(count($check_tempo_explode) != 2 || $check_tempo_explode[0] < 1 || $check_tempo_explode[1] < 1)
{
return false;
}
if(!is_valid_note($check_tempo_explode[0]))
{
return false;
}
return true;
}
function is_valid_bars($check_bars)
{
if(!intval($check_bars))
{
return false;
}
$check_bars = intval($check_bars);
if($check_bars < 1 || $check_bars > 64)
{
return false;
}
return true;
}
function is_valid_dynamic($check_dynamic)
{
if(!is_string($check_dynamic))
{
return false;
}
$allowed_dynamics_regex = "/^(p{1,5}|f{1,5}|m(p|f)|s(p{1,2}|f{1,2})|fp|(s|r)fz)$/";
if(!preg_match($allowed_dynamics_regex, $check_dynamic))
{
return false;
}
return true;
}
function validate_get_parameter()
{
if(!(isset($_GET["time"]) && is_valid_time($_GET["time"])))
{
$_GET["time"] = "4/4";
}
$_GET["time"] = remove_whitespace($_GET["time"]);
if(!(isset($_GET["tempo"]) && is_valid_tempo($_GET["tempo"])))
{
$_GET["tempo"] = "4=90";
}
$_GET["tempo"] = remove_whitespace($_GET["tempo"]);
if(!(isset($_GET["bars"]) && is_valid_bars($_GET["bars"])))
{
$_GET["bars"] = "2";
}
if(!(isset($_GET["dynamic_beat"]) && is_valid_dynamic($_GET["dynamic_beat"])))
{
$_GET["dynamic_beat"] = "pp";
}
if(!(isset($_GET["dynamic_rhythm"]) && is_valid_dynamic($_GET["dynamic_rhythm"])))
{
$_GET["dynamic_rhythm"] = "ff";
}
if(!(isset($_GET["id"]) && is_string($_GET["id"]) && strlen($_GET["id"]) > 1))
{
$_GET["id"] = md5(random_bytes(60));
}
}
?>

View file

@ -0,0 +1,68 @@
# This file is distributed under the same license as the rhythm package.
# Serguzim <serguzim@msrg.cc>, 2020.
#
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1 VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-05 21:21+0100\n"
"PO-Revision-Date: 2020-03-05 18:53+0100\n"
"Last-Translator: <serguzim@msrg.cc>\n"
"Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: templates/index.tpl.php:26
msgid "Solution"
msgstr "Lösung"
#: templates/index.tpl.php:37
msgid "Generate this rhythm"
msgstr "Erzeuge diesen Rhythmus"
#: templates/index.tpl.php:38
msgid "Generate new rhythm"
msgstr "Erzeuge neues Rhythmus"
#: templates/index.tpl.php:43
msgid "Anything is possible. Used as seed for the rhythm."
msgstr "Alles ist möglich. Wird als Grundlage für den Rhythmus verwendet."
#: templates/index.tpl.php:47
msgid "Time Signature"
msgstr "Taktvorzeichen"
#: templates/index.tpl.php:50 templates/index.tpl.php:58
msgid "Examples"
msgstr "Beispiele"
#: templates/index.tpl.php:54
msgid "Tempo"
msgstr "Tempo"
#: templates/index.tpl.php:58
msgid "click"
msgstr "klick"
#: templates/index.tpl.php:66
msgid "Bars"
msgstr "Takte"
#: templates/index.tpl.php:69
#, php-format
msgid "Anything between %s and %s"
msgstr "Alles zwischen %s und %s"
#: templates/index.tpl.php:73
msgid "Dynamic for Beat"
msgstr "Dynamik für den Beat"
#: templates/index.tpl.php:80
msgid "Dynamic for Rhythm"
msgstr "Dynamik für den Rhytmus"
#~ msgid "Submit"
#~ msgstr "Absenden"

View file

@ -0,0 +1,68 @@
# This file is distributed under the same license as the rhythm package.
# Serguzim <serguzim@msrg.cc>, 2020.
#
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1 VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-05 21:21+0100\n"
"PO-Revision-Date: 2020-03-05 19:14+0100\n"
"Last-Translator: <serguzim@msrg.cc>\n"
"Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: templates/index.tpl.php:26
msgid "Solution"
msgstr "Solution"
#: templates/index.tpl.php:37
msgid "Generate this rhythm"
msgstr "Generate this rhythm"
#: templates/index.tpl.php:38
msgid "Generate new rhythm"
msgstr "Generate new rhythm"
#: templates/index.tpl.php:43
msgid "Anything is possible. Used as seed for the rhythm."
msgstr "Anything is possible. Used as seed for the rhythm."
#: templates/index.tpl.php:47
msgid "Time Signature"
msgstr "Time Signature"
#: templates/index.tpl.php:50 templates/index.tpl.php:58
msgid "Examples"
msgstr "Examples"
#: templates/index.tpl.php:54
msgid "Tempo"
msgstr "Tempo"
#: templates/index.tpl.php:58
msgid "click"
msgstr "click"
#: templates/index.tpl.php:66
msgid "Bars"
msgstr "Bars"
#: templates/index.tpl.php:69
#, php-format
msgid "Anything between %s and %s"
msgstr "Anything between %s and %s"
#: templates/index.tpl.php:73
msgid "Dynamic for Beat"
msgstr "Dynamic for Beat"
#: templates/index.tpl.php:80
msgid "Dynamic for Rhythm"
msgstr "Dynamic for Rhythm"
#~ msgid "Submit"
#~ msgstr "Submit"

67
locale/rhythm.pot Normal file
View file

@ -0,0 +1,67 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-05 21:22+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: templates/index.tpl.php:26
msgid "Solution"
msgstr ""
#: templates/index.tpl.php:37
msgid "Generate this rhythm"
msgstr ""
#: templates/index.tpl.php:38
msgid "Generate new rhythm"
msgstr ""
#: templates/index.tpl.php:43
msgid "Anything is possible. Used as seed for the rhythm."
msgstr ""
#: templates/index.tpl.php:47
msgid "Time Signature"
msgstr ""
#: templates/index.tpl.php:50 templates/index.tpl.php:58
msgid "Examples"
msgstr ""
#: templates/index.tpl.php:54
msgid "Tempo"
msgstr ""
#: templates/index.tpl.php:58
msgid "click"
msgstr ""
#: templates/index.tpl.php:66
msgid "Bars"
msgstr ""
#: templates/index.tpl.php:69
#, php-format
msgid "Anything between %s and %s"
msgstr ""
#: templates/index.tpl.php:73
msgid "Dynamic for Beat"
msgstr ""
#: templates/index.tpl.php:80
msgid "Dynamic for Rhythm"
msgstr ""

72
messages.po Normal file
View file

@ -0,0 +1,72 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-05 21:19+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: templates/index.tpl.php:26 templates/index.tpl.php:67
msgid "Solution"
msgstr ""
#: templates/index.tpl.php:37
msgid "Generate this rhythm"
msgstr ""
#: templates/index.tpl.php:38
msgid "Generate new rhythm"
msgstr ""
#: templates/index.tpl.php:43 templates/index.tpl.php:19
msgid "Anything is possible. Used as seed for the rhythm."
msgstr ""
#: templates/index.tpl.php:47 templates/index.tpl.php:23
msgid "Time Signature"
msgstr ""
#: templates/index.tpl.php:50 templates/index.tpl.php:58
#: templates/index.tpl.php:26 templates/index.tpl.php:34
msgid "Examples"
msgstr ""
#: templates/index.tpl.php:54 templates/index.tpl.php:30
msgid "Tempo"
msgstr ""
#: templates/index.tpl.php:58 templates/index.tpl.php:34
msgid "click"
msgstr ""
#: templates/index.tpl.php:66 templates/index.tpl.php:42
msgid "Bars"
msgstr ""
#: templates/index.tpl.php:69 templates/index.tpl.php:45
#, php-format
msgid "Anything between %s and %s"
msgstr ""
#: templates/index.tpl.php:73 templates/index.tpl.php:49
msgid "Dynamic for Beat"
msgstr ""
#: templates/index.tpl.php:80 templates/index.tpl.php:56
msgid "Dynamic for Rhythm"
msgstr ""
#: templates/index.tpl.php:63
msgid "Submit"
msgstr ""

BIN
static/res/tempo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

89
templates/index.tpl.php Normal file
View file

@ -0,0 +1,89 @@
<html>
<head>
<title>Rhythm Thing</title>
<link rel="stylesheet" href="/static/fontawesome/css/all.min.css">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<script src="/static/js/jquery.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container mt-3">
<div class="row">
<h1 class="mx-auto">Quick Rhythm Maker</h1>
</div>
<hr>
<div class="row mb-3">
<div class="col-12">
<audio style="width: 100%;" controls>
<source src="audio.php?file_name=<?php echo($file_name) ?>" type="audio/ogg">
Your browser does not support the audio element.
</audio>
</div>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-warning" type="button" data-toggle="collapse" data-target="#collapse-solution">
<?php echo(_("Solution")) ?>
</button>
<br>
<div class="collapse" id="collapse-solution">
<img class="py-3" src="./tmp/<?php echo($file_name) ?>.png">
</div>
</div>
</div>
<hr>
<div class="row">
<form class="col-12" action="" method="get">
<button class="btn btn-primary" type="submit"><?php echo(_("Generate this rhythm")) ?></button>
<button class="btn btn-secondary" onclick="document.getElementById('id').value ='';" type="submit"><?php echo(_("Generate new rhythm")) ?></button>
<div class="form-group">
ID
<input type="text" class="form-control" name="id" id="id" value="<?php echo($id); ?>" />
<small>
<?php echo(_("Anything is possible. Used as seed for the rhythm.")) ?>
</small>
</div>
<div class="form-group">
<?php echo(_("Time Signature")) ?>
<input type="text" class="form-control" name="time" value="<?php echo($time); ?>" />
<small>
<?php echo(_("Examples")) ?>: 4/4, 3/4, 6/8, 2/2, 13/8, 11/16, 2/4
</small>
</div>
<div class="form-group">
<?php echo(_("Tempo")) ?>
<input type="text" class="form-control" name="tempo" value="<?php echo($tempo); ?>" />
<small>
<a data-toggle="collapse" href="#collapse-tempo" role="button">
<?php echo(_("Examples")) ?> (<?php echo(_("click")) ?>)
</a>
</small>
<div class="collapse" id="collapse-tempo">
<img src="./static/res/tempo.png">
</div>
</div>
<div class="form-group">
<?php echo(_("Bars")) ?>
<input type="number" class="form-control" min="1" max="64" name="bars" value="<?php echo($bars); ?>" />
<small>
<?php printf(_("Anything between %s and %s"), 1, 64) ?>
</small>
</div>
<div class="form-group">
<?php echo(_("Dynamic for Beat")) ?>
<input type="text" class="form-control" name="dynamic_beat" value="<?php echo($dynamic_beat); ?>" />
<small>
ppppp, pppp, ppp, pp, p, mp, mf, f, ff, fff, ffff, fffff, fp, sf, sff, sp, spp, sfz, rfz
</small>
</div>
<div class="form-group">
<?php echo(_("Dynamic for Rhythm")) ?>
<input type="text" class="form-control" name="dynamic_rhythm" value="<?php echo($dynamic_rhythm); ?>" />
<small>
ppppp, pppp, ppp, pp, p, mp, mf, f, ff, fff, ffff, fffff, fp, sf, sff, sp, spp, sfz, rfz
</small>
</div>
</form>
</div>
</div>
</body>