diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | package.json | 22 | ||||
| -rw-r--r-- | src/c/app.c | 4 | ||||
| -rw-r--r-- | src/c/config.c | 123 | ||||
| -rw-r--r-- | src/c/config.h | 26 | ||||
| -rw-r--r-- | src/c/metronome.c | 34 | ||||
| -rw-r--r-- | src/c/metronome.h | 2 | ||||
| -rw-r--r-- | src/pkjs/config.json | 73 | ||||
| -rw-r--r-- | src/pkjs/index.js | 3 |
9 files changed, 271 insertions, 18 deletions
@@ -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(¬e, 1, 0); + speaker_play_notes(¬e, 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); |
