diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index f035203..23d2a97 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -119,7 +119,7 @@ make -f Makefile.dev release-arch
- [x] Find and fix opengl errors 0500 ?
- [ ] Extra features
- [x] `--auto-random-cycle=4`
- - [ ] Arrows (up-down: bpm / left-right: cycle)
+ - [x] Arrows (up-down: bpm / left-right: cycle)
- [ ] Save states (numkey: load / shift + numkey: save)
- [ ] Configurable key codes
- [ ] Key codes as inputs
diff --git a/README.md b/README.md
index 392c4a3..b5806ce 100644
--- a/README.md
+++ b/README.md
@@ -157,11 +157,15 @@ make install
When running, the following hotkeys are available:
-* Esc: Exit window
-* R: Randomize internal values
-* 0: Reset internal values to 0
-* D: Demo mode On/Off
-* A: Auto Random mode On/Off
+| Hotkey | Function |
+| ------ | -------- |
+| Esc | Exit FORGE |
+| R | Randomize internal values |
+| Shift + R | Reset internal values to 0 |
+| D | Demo mode On/Off |
+| A | Auto Random mode On/Off |
+| ← / → | Auto Random Cycle -/+ 1 |
+| ↑ / ↓ | BPM +/- 1 |
### CLI arguments
diff --git a/default/forge_project.cfg b/default/forge_project.cfg
index 430fd97..c807a5f 100644
--- a/default/forge_project.cfg
+++ b/default/forge_project.cfg
@@ -37,6 +37,8 @@ UNIFORM_IN_FPS_PREFIX=iInputFPS
UNIFORM_DEMO=iDemo
# 0/1 if auto random
UNIFORM_AUTORAND=iAutoRand
+# auto random cycle length
+UNIFORM_AUTORANDCYCLE=iAutoRandCycle
# Current page
UNIFORM_PAGE=iPage
# Current selected shader
diff --git a/default/inc_cp437.glsl b/default/inc_cp437.glsl
index 1f2d2a3..a0b13bf 100644
--- a/default/inc_cp437.glsl
+++ b/default/inc_cp437.glsl
@@ -372,6 +372,20 @@ float write_int(vec2 uv, vec2 pos, uint value, uint magnitude)
return d;
}
+float write_int_left(vec2 uv, vec2 pos, uint value, uint magnitude)
+{
+ int i;
+ uint m = 1;
+ float d = 0;
+ for (i = 0; i < magnitude; i++) {
+ if (i == 0 || value >= m) {
+ pos.x += 1;
+ m *= 10u;
+ }
+ }
+ return write_int(uv, pos, value, magnitude);
+}
+
int read(sampler2D tex, vec2 uv, float k, int d, float t)
{
float inv_k = 1 / k;
diff --git a/default/inc_debug.glsl b/default/inc_debug.glsl
index a761932..4ebec7f 100644
--- a/default/inc_debug.glsl
+++ b/default/inc_debug.glsl
@@ -15,6 +15,7 @@ uniform int iInputFormat2;
uniform int iDemo;
uniform int iAutoRand;
+uniform int iAutoRandCycle;
uniform int iPage;
uniform int iSelected;
@@ -225,22 +226,22 @@ vec4 debug(vec2 vUV)
float x = 0;
x = -15;
- f += write_5(uv3, vec2(x,13), texts[0]);
- f += write_int(uv3, vec2(x - 3.5,13), iFPS, 3);
+ f += write_5(uv3, vec2(x - 4,13), texts[0]);
+ f += write_int(uv3, vec2(x + 1, 13), iFPS, 3);
v = min(1, iFPS/60.0);
f += h_rect(uv3, vec2(x, 12), vec2(4, 0.5), 0.2);
f += rect(uv3, vec2(x + 4 * v - 4, 12), vec2(4 * v, 0.4));
x = 0;
- f += write_5(uv3, vec2(x,13), texts[1]);
- f += write_int(uv3, vec2(x - 3.5,13), int(iTempo), 3);
+ f += write_5(uv3, vec2(x - 4,13), texts[1]);
+ f += write_int(uv3, vec2(x + 1,13), int(iTempo), 3);
v = fract(iBeats);
f += h_rect(uv3, vec2(x, 12), vec2(4, 0.5), 0.2);
f += rect(uv3, vec2(x + 4 * v - 4, 12), vec2(4 * v, 0.4));
x = 15;
- f += write_5(uv3, vec2(x,13), texts[2]);
- f += write_int(uv3, vec2(x - 5.5,13), int(iTime), 5);
+ f += write_5(uv3, vec2(x - 4,13), texts[2]);
+ f += write_int(uv3, vec2(x - 1,13), int(iTime), 5);
v = fract(iTime);
f += h_rect(uv3, vec2(x, 12), vec2(4, 0.5), 0.2);
f += rect(uv3, vec2(x + 4 * v - 4, 12), vec2(4 * v, 0.4));
@@ -248,6 +249,7 @@ vec4 debug(vec2 vUV)
if (iAutoRand > 0) {
f += write_5(uv3, vec2(-4,-15), iDemo > 0 ? texts[3] : texts[4]);
f += write_5(uv3, vec2(0,-15), texts[5]);
+ f += write_int_left(uv3, vec2(3, -15), iAutoRandCycle, 3);
} else {
f += write_5(uv3, vec2(-2,-15), iDemo > 0 ? texts[3] : texts[4]);
}
diff --git a/sample/forge_project.cfg b/sample/forge_project.cfg
index e47fca2..e3af011 100644
--- a/sample/forge_project.cfg
+++ b/sample/forge_project.cfg
@@ -33,8 +33,6 @@ UNIFORM_BEATS=iBeats
UNIFORM_FPS=iFPS
# 0/1 if demo
UNIFORM_DEMO=iDemo
-# 0/1 if auto random
-UNIFORM_AUTORAND=iAutoRand
# Seed for shader X
UNIFORM_SEED_PREFIX=iSeed
diff --git a/sample/frag1.glsl b/sample/frag1.glsl
index a0ab544..5faa9cc 100644
--- a/sample/frag1.glsl
+++ b/sample/frag1.glsl
@@ -10,7 +10,6 @@ uniform float iTempo; // current tempo in bpm
uniform float iBeats; // elapsed beats since last tempo reset
uniform int iFPS; // output window frames per seconds
uniform int iDemo; // 0/1 if demo mode
-uniform int iAutoRand; // 0/1 if auto random mode
uniform int iSeed1; // a random seed assigned at start
uniform vec2 iResolution; // output window resolution in pixels
uniform vec3 iMidi1_1[20]; // all midi inputs defined
diff --git a/src/args.c b/src/args.c
index bc29408..e45625e 100644
--- a/src/args.c
+++ b/src/args.c
@@ -127,7 +127,7 @@ void args_parse(Parameters *params, int argc, char **argv) {
params->base_tempo = 60.0f;
params->demo = false;
params->auto_random = false;
- params->auto_random_cycles = 4;
+ params->auto_random_cycle = 4;
params->video_in.length = 0;
params->video_size = 0;
params->internal_size = 720;
@@ -173,8 +173,8 @@ void args_parse(Parameters *params, int argc, char **argv) {
} else if (is_arg(arg, "-nar") || is_arg(arg, "--no-auto-random")) {
params->auto_random = false;
} else if (is_arg(arg, "-arc") || is_arg(arg, "--auto-random-cycle")) {
- params->auto_random_cycles = parse_uint(arg, value);
- if (params->auto_random_cycles == 0) {
+ params->auto_random_cycle = parse_uint(arg, value);
+ if (params->auto_random_cycle == 0) {
invalid_value(arg, value);
}
} else if (is_arg(arg, "-v") || is_arg(arg, "--video-in")) {
diff --git a/src/forge.c b/src/forge.c
index 6807d91..b572206 100644
--- a/src/forge.c
+++ b/src/forge.c
@@ -59,7 +59,7 @@ static void compute_fps(bool trace_fps) {
static void init_context(const Parameters *params, unsigned int in_count) {
state_init(context, &project.state_config, params->demo, params->auto_random,
- params->auto_random_cycles, params->base_tempo, params->state_file,
+ params->auto_random_cycle, params->base_tempo, params->state_file,
params->load_state);
context->monitor = params->monitor;
diff --git a/src/shaders.c b/src/shaders.c
index faffcb1..ddf54d1 100644
--- a/src/shaders.c
+++ b/src/shaders.c
@@ -280,6 +280,9 @@ static void init_single_program(ShaderProgram *program, unsigned int i,
program->iautorand_locations[i] = glGetUniformLocation(
program->programs[i],
config_file_get_str(config, "UNIFORM_AUTORAND", "iAutoRand"));
+ program->iautorandcycle_locations[i] = glGetUniformLocation(
+ program->programs[i],
+ config_file_get_str(config, "UNIFORM_AUTORANDCYCLE", "iAutoRandCycle"));
program->ipage_locations[i] = glGetUniformLocation(
program->programs[i],
config_file_get_str(config, "UNIFORM_PAGE", "iPage"));
@@ -545,6 +548,8 @@ static void use_program(const ShaderProgram *program, int i, bool output,
write_uniform_1i(program->idemo_locations[i], context->demo ? 1 : 0);
write_uniform_1i(program->iautorand_locations[i],
context->auto_random ? 1 : 0);
+ write_uniform_1i(program->iautorandcycle_locations[i],
+ context->auto_random_cycle);
write_uniform_1i(program->ipage_locations[i], context->page);
write_uniform_1i(program->iselected_locations[i], context->selected + 1);
write_uniform_2f(program->ires_locations[i], &context->resolution);
diff --git a/src/state.c b/src/state.c
index 42d02ba..bea117c 100644
--- a/src/state.c
+++ b/src/state.c
@@ -301,6 +301,26 @@ void state_key_event(SharedContext *context, const StateConfig *state_config,
log_info(
(context->auto_random ? "[A] Auto Random OFF" : "[A] Auto Random ON"));
context->auto_random = !context->auto_random;
+ } else if (code == 263) {
+ // LEFT ARROW
+ if (context->auto_random_cycle > 1) {
+ context->auto_random_cycle -= 1;
+ }
+ log_info("[LEFT] Auto Random Cycle: %d", context->auto_random_cycle);
+ } else if (code == 262) {
+ // RIGHT ARROW
+ context->auto_random_cycle += 1;
+ log_info("[RIGHT] Auto Random Cycle: %d", context->auto_random_cycle);
+ } else if (code == 265) {
+ // UP ARROW
+ tempo_set(&context->tempo, context->tempo.tempo + 1);
+ log_info("[UP] Tempo: %f", context->tempo);
+ } else if (code == 264) {
+ // DOWN ARROW
+ if (context->tempo.tempo > 0) {
+ tempo_set(&context->tempo, context->tempo.tempo - 1);
+ }
+ log_info("[DOWN] Tempo: %f", context->tempo);
} else {
log_info("[%d] No hotkey defined", code);
}
@@ -353,7 +373,7 @@ bool state_background_write(SharedContext *context,
last_active = beat_active;
change = tempo_progress(&context->tempo,
- (double)context->auto_random_cycles) < 0.5;
+ (double)context->auto_random_cycle) < 0.5;
if (context->auto_random && change && !last_change) {
state_randomize(context, state_config);
@@ -416,7 +436,7 @@ void state_init(SharedContext *context, const StateConfig *state_config,
tempo_set(&context->tempo, base_tempo);
context->demo = demo;
context->auto_random = auto_random;
- context->auto_random_cycles = auto_random_cycles;
+ context->auto_random_cycle = auto_random_cycles;
context->state.length = state_config->select_frag_codes.length;
memset(context->state.values, 0, sizeof(context->state.values));
diff --git a/src/tempo.c b/src/tempo.c
index c7418e0..fb15833 100644
--- a/src/tempo.c
+++ b/src/tempo.c
@@ -99,8 +99,17 @@ static void add_tap_to_chain(Tempo *tempo, long t) {
}
void tempo_set(Tempo *tempo, float value) {
+ long t;
+ long progress;
+
+ t = now();
+
+ progress = (t - tempo->last_reset) % tempo->beat_length;
+
tempo->tempo = value;
tempo->beat_length = 60000.0 / value;
+
+ reset_tap_chain(tempo, t - progress);
}
void tempo_tap(Tempo *tempo) {
diff --git a/src/types.h b/src/types.h
index ab5730e..7867e26 100644
--- a/src/types.h
+++ b/src/types.h
@@ -43,7 +43,7 @@ typedef struct Parameters {
float base_tempo;
bool demo;
bool auto_random;
- unsigned int auto_random_cycles;
+ unsigned int auto_random_cycle;
StringArray video_in;
unsigned int video_size;
unsigned int internal_size;
@@ -105,6 +105,7 @@ typedef struct ShaderProgram {
GLuint iinfps_locations[ARRAY_SIZE];
GLuint idemo_locations[ARRAY_SIZE];
GLuint iautorand_locations[ARRAY_SIZE];
+ GLuint iautorandcycle_locations[ARRAY_SIZE];
GLuint iseed_locations[ARRAY_SIZE];
GLuint istate_locations[ARRAY_SIZE];
GLuint ipage_locations[ARRAY_SIZE];
@@ -183,7 +184,7 @@ typedef struct SharedContext {
vec3 values[ARRAY_SIZE];
bool demo;
bool auto_random;
- unsigned int auto_random_cycles;
+ unsigned int auto_random_cycle;
unsigned int seeds[MAX_FRAG];
bool monitor;