summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--package.json22
-rw-r--r--src/c/app.c4
-rw-r--r--src/c/config.c123
-rw-r--r--src/c/config.h26
-rw-r--r--src/c/metronome.c34
-rw-r--r--src/c/metronome.h2
-rw-r--r--src/pkjs/config.json73
-rw-r--r--src/pkjs/index.js3
9 files changed, 271 insertions, 18 deletions
diff --git a/.gitignore b/.gitignore
index e5f35ea..79c63e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
build/
+node_modules/
.lock-waf_linux_build
+package-lock.json
diff --git a/package.json b/package.json
index 45fcf13..ecc4a9a 100644
--- a/package.json
+++ b/package.json
@@ -1,22 +1,34 @@
{
"name": "metronommle",
"author": "Reiner Herrmann",
- "version": "0.1.0",
- "keywords": ["pebble-app"],
+ "version": "0.2.0",
+ "keywords": [
+ "pebble-app"
+ ],
"private": true,
- "dependencies": {},
+ "capabilities": [
+ "configurable"
+ ],
+ "dependencies": {
+ "@rebble/clay": "^1.0.10"
+ },
"pebble": {
"displayName": "Metronommle",
"uuid": "e50eb510-a9eb-4d30-94f6-cbec496ded0b",
"sdkVersion": "3",
- "enableMultiJS": false,
+ "enableMultiJS": true,
"targetPlatforms": [
"emery"
],
"watchapp": {
"watchface": false
},
- "messageKeys": [],
+ "messageKeys": [
+ "bpm",
+ "beats",
+ "volume",
+ "beat_indicators[3]"
+ ],
"resources": {
"media": [
{
diff --git a/src/c/app.c b/src/c/app.c
index 2ce9996..d74c5d3 100644
--- a/src/c/app.c
+++ b/src/c/app.c
@@ -4,6 +4,7 @@
#include <pebble.h>
#include "metronome.h"
+#include "config.h"
static Window *app_window;
static ActionBarLayer *action_bar;
@@ -86,6 +87,8 @@ static void primary_action_bar() {
}
static void init() {
+ config_init();
+
icon_left_arrows = gbitmap_create_with_resource(RESOURCE_ID_LEFT_ARROWS);
icon_right_arrows = gbitmap_create_with_resource(RESOURCE_ID_RIGHT_ARROWS);
icon_plus = gbitmap_create_with_resource(RESOURCE_ID_PLUS);
@@ -104,6 +107,7 @@ static void init() {
}
static void deinit() {
+ config_deinit();
window_destroy(app_window);
}
diff --git a/src/c/config.c b/src/c/config.c
new file mode 100644
index 0000000..0791b85
--- /dev/null
+++ b/src/c/config.c
@@ -0,0 +1,123 @@
+/* Copyright (C) 2026 Reiner Herrmann
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <pebble.h>
+#include "config.h"
+#include "metronome.h"
+
+struct Configuration {
+ uint8_t version;
+ uint16_t bpm;
+ uint8_t beats;
+ uint8_t volume;
+ uint8_t beat_indicators;
+} __attribute__((__packed__));
+
+#define SETTINGS_KEY 1
+#define CONFIG_VERSION 1
+
+static struct Configuration configuration;
+
+static void config_persist() {
+ persist_write_data(SETTINGS_KEY, &configuration, sizeof(configuration));
+}
+
+static void config_load_defaults() {
+ configuration.version = CONFIG_VERSION;
+ configuration.bpm = 90;
+ configuration.beats = 4;
+ configuration.volume = 100;
+ configuration.beat_indicators = 0xff;
+}
+
+static void config_load() {
+ config_load_defaults();
+
+ /* buffer remains unchanged if no data is persisted */
+ persist_read_data(SETTINGS_KEY, &configuration, sizeof(configuration));
+
+ if (configuration.version != CONFIG_VERSION) {
+ /* unknown persisted configuration format */
+ config_load_defaults();
+ }
+}
+
+static void config_inbox_received_handler(DictionaryIterator *iter, void *context) {
+ struct Configuration new_configuration;
+ memcpy(&new_configuration, &configuration, sizeof(configuration));
+
+ Tuple *bpm_tuple = dict_find(iter, MESSAGE_KEY_bpm);
+ if (bpm_tuple) {
+ int32_t bpm = bpm_tuple->value->int32;
+ configuration.bpm = (uint16_t) bpm;
+ }
+
+ Tuple *beats_tuple = dict_find(iter, MESSAGE_KEY_beats);
+ if (beats_tuple) {
+ int32_t beats = beats_tuple->value->int32;
+ configuration.beats = (uint8_t) beats;
+ }
+
+ Tuple *volume_tuple = dict_find(iter, MESSAGE_KEY_volume);
+ if (volume_tuple) {
+ int32_t volume = volume_tuple->value->int32;
+ configuration.volume = (uint8_t) volume;
+ }
+
+ const int num_beat_indicators = 3;
+ for (int i=0; i<num_beat_indicators; i++) {
+ Tuple *beat_indicator = dict_find(iter, MESSAGE_KEY_beat_indicators + i);
+ if (beat_indicator) {
+ bool val = beat_indicator->value->uint8;
+ /* clear bit */
+ configuration.beat_indicators &= ~(1 << i);
+ /* set value into bit */
+ configuration.beat_indicators |= (val << i);
+ }
+ }
+
+ if (memcmp(&new_configuration, &configuration, sizeof(configuration)) != 0) {
+ /* only persist if something changed */
+ config_persist();
+
+ update_display();
+ }
+}
+
+void config_init() {
+ config_load();
+
+ app_message_register_inbox_received(config_inbox_received_handler);
+ app_message_open(128, 128);
+}
+
+void config_deinit() {
+ app_message_deregister_callbacks();
+}
+
+uint16_t config_get_bpm() {
+ return configuration.bpm;
+}
+
+void config_set_bpm(uint16_t bpm) {
+ configuration.bpm = bpm;
+ config_persist();
+}
+
+uint8_t config_get_beats() {
+ return configuration.beats;
+}
+
+void config_set_beats(uint8_t beats) {
+ configuration.beats = beats;
+ config_persist();
+}
+
+uint8_t config_get_volume() {
+ return configuration.volume;
+}
+
+bool config_is_indicator_enabled(enum BeatIndicator indicator) {
+ return configuration.beat_indicators & (1 << indicator);
+}
diff --git a/src/c/config.h b/src/c/config.h
new file mode 100644
index 0000000..899c289
--- /dev/null
+++ b/src/c/config.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2026 Reiner Herrmann
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <pebble.h>
+
+enum BeatIndicator {
+ FLASHING = 0,
+ VIBRATION,
+ BEEP,
+};
+
+void config_init();
+void config_deinit();
+
+uint16_t config_get_bpm();
+void config_set_bpm(uint16_t bpm);
+uint8_t config_get_beats();
+void config_set_beats(uint8_t beats);
+uint8_t config_get_volume();
+bool config_is_indicator_enabled(enum BeatIndicator);
+
+#endif
diff --git a/src/c/metronome.c b/src/c/metronome.c
index f7a5bd3..58fdf18 100644
--- a/src/c/metronome.c
+++ b/src/c/metronome.c
@@ -3,6 +3,7 @@
*/
#include <pebble.h>
+#include "config.h"
static Window *metronome_window;
static Layer *draw_layer;
@@ -11,7 +12,7 @@ static TextLayer *bpm_text;
static AppTimer *beat_timer;
#define BPM_TO_MS(bpm) (60000/(bpm))
-#define IS_FIRST_BEAT() (current_beat % beats == 0)
+#define IS_FIRST_BEAT() (current_beat % config_get_beats() == 0)
#define COLOR_BG GColorWhite
#define COLOR_BG_INV GColorBlack
@@ -20,23 +21,17 @@ static AppTimer *beat_timer;
#define NOTE_C4 60
#define NOTE_C5 72
-static int bpm = 90;
-static int beats = 4;
static int current_beat = -1;
static bool playing = true;
-static bool flash_enabled = true;
-static bool vibrate_enabled = true;
-static bool beep_enabled = true;
-
static void restore_background_color() {
window_set_background_color(metronome_window, COLOR_BG);
}
static void metronome_flash() {
static const int flash_duration = 50;
- if (!flash_enabled) {
+ if (!config_is_indicator_enabled(FLASHING)) {
return;
}
window_set_background_color(metronome_window, COLOR_BG_INV);
@@ -44,7 +39,7 @@ static void metronome_flash() {
}
static void metronome_vibrate() {
- if (!vibrate_enabled) {
+ if (!config_is_indicator_enabled(VIBRATION)) {
return;
}
if (IS_FIRST_BEAT()) {
@@ -57,7 +52,7 @@ static void metronome_vibrate() {
static void metronome_beep() {
#if defined(PBL_SPEAKER)
- if (!beep_enabled || speaker_is_muted()) {
+ if (!config_is_indicator_enabled(BEEP) || speaker_is_muted()) {
return;
}
const SpeakerNote note = {
@@ -67,12 +62,12 @@ static void metronome_beep() {
.velocity = 0,
};
- speaker_play_notes(&note, 1, 0);
+ speaker_play_notes(&note, 1, config_get_volume());
#endif
}
static void beat_timer_callback() {
- beat_timer = app_timer_register(BPM_TO_MS(bpm), beat_timer_callback, NULL);
+ beat_timer = app_timer_register(BPM_TO_MS(config_get_bpm()), beat_timer_callback, NULL);
current_beat++;
@@ -85,11 +80,12 @@ static void beat_timer_callback() {
static void update_bpm_text() {
static char bpm_buf[6];
- snprintf(bpm_buf, sizeof(bpm_buf), "%d", bpm);
+ snprintf(bpm_buf, sizeof(bpm_buf), "%d", config_get_bpm());
text_layer_set_text(bpm_text, bpm_buf);
}
static void beat_indicators_update_proc(Layer *layer, GContext *ctx) {
+ int beats = config_get_beats();
GRect bounds = layer_get_unobstructed_bounds(layer);
const int rect_radius = 10;
@@ -170,36 +166,48 @@ Window *create_metronome_window() {
return metronome_window;
}
+void update_display() {
+ update_bpm_text();
+}
+
void increase_bpm() {
+ uint16_t bpm = config_get_bpm();
bpm++;
if (bpm > 600) {
bpm = 600;
}
+ config_set_bpm(bpm);
APP_LOG(APP_LOG_LEVEL_DEBUG, "increasing bpm to: %d", bpm);
update_bpm_text();
}
void decrease_bpm() {
+ uint16_t bpm = config_get_bpm();
bpm--;
if (bpm <= 0) {
bpm = 0;
}
+ config_set_bpm(bpm);
APP_LOG(APP_LOG_LEVEL_DEBUG, "decreasing bpm to: %d", bpm);
update_bpm_text();
}
void increase_beats() {
+ uint8_t beats = config_get_beats();
beats++;
if (beats > 16) {
beats = 16;
}
+ config_set_beats(beats);
APP_LOG(APP_LOG_LEVEL_DEBUG, "increasing beats to: %d", beats);
}
void decrease_beats() {
+ uint8_t beats = config_get_beats();
beats--;
if (beats < 1) {
beats = 1;
}
+ config_set_beats(beats);
APP_LOG(APP_LOG_LEVEL_DEBUG, "decreasing beats to: %d", beats);
}
diff --git a/src/c/metronome.h b/src/c/metronome.h
index ee9e5f0..15927ff 100644
--- a/src/c/metronome.h
+++ b/src/c/metronome.h
@@ -9,6 +9,8 @@ Window *create_metronome_window();
void beat_timer_callback();
+void update_display();
+
void increase_bpm();
void decrease_bpm();
void increase_beats();
diff --git a/src/pkjs/config.json b/src/pkjs/config.json
new file mode 100644
index 0000000..d672ea7
--- /dev/null
+++ b/src/pkjs/config.json
@@ -0,0 +1,73 @@
+[
+ {
+ "type": "section",
+ "items": [
+ {
+ "type": "heading",
+ "defaultValue": "Beats"
+ },
+ {
+ "type": "slider",
+ "messageKey": "bpm",
+ "defaultValue": 90,
+ "label": "Per Minute",
+ "min": 1,
+ "max": 200
+ },
+ {
+ "type": "slider",
+ "messageKey": "beats",
+ "defaultValue": 4,
+ "label": "Per Bar",
+ "min": 1,
+ "max": 16
+ }
+ ]
+ },
+ {
+ "type": "section",
+ "items": [
+ {
+ "type": "heading",
+ "defaultValue": "Settings"
+ },
+ {
+ "type": "slider",
+ "messageKey": "volume",
+ "label": "Volume",
+ "defaultValue": 100,
+ "min": 1,
+ "max": 100,
+ "capabilities": [
+ "PLATFORM_EMERY"
+ ]
+ },
+ {
+ "type": "slider",
+ "messageKey": "volume",
+ "label": "Volume",
+ "defaultValue": 100,
+ "min": 1,
+ "max": 100,
+ "capabilities": [
+ "PLATFORM_FLINT"
+ ]
+ },
+ {
+ "type": "checkboxgroup",
+ "messageKey": "beat_indicators",
+ "label": "Beat indicators",
+ "defaultValue": [true, true, true],
+ "options": [
+ "Flashing",
+ "Vibration",
+ "Beep"
+ ]
+ }
+ ]
+ },
+ {
+ "type": "submit",
+ "defaultValue": "Save"
+ }
+]
diff --git a/src/pkjs/index.js b/src/pkjs/index.js
new file mode 100644
index 0000000..3f7cff9
--- /dev/null
+++ b/src/pkjs/index.js
@@ -0,0 +1,3 @@
+var Clay = require('@rebble/clay');
+var clayConfig = require('./config.json');
+var clay = new Clay(clayConfig);