diff --git a/firmware/apps/_samples/audio/prj.conf b/firmware/apps/_samples/audio/prj.conf index e6e1787..7c18219 100644 --- a/firmware/apps/_samples/audio/prj.conf +++ b/firmware/apps/_samples/audio/prj.conf @@ -23,7 +23,8 @@ CONFIG_SHELL_BACKEND_SERIAL=y # CONFIG_MCUMGR_GRP_FS_CHECKSUM_HASH=y # Lasertag-spezifische Konfiguration +CONFIG_LASERTAG_UTILS=y CONFIG_FS_MGMT=y -CONFIG_FS_MGMT_LOG_LEVEL_DBG=y +CONFIG_FS_MGMT_LOG_LEVEL_DBG=n CONFIG_AUDIO=y -CONFIG_AUDIO_LOG_LEVEL_DBG=y \ No newline at end of file +CONFIG_AUDIO_LOG_LEVEL_DBG=y diff --git a/firmware/apps/_samples/audio/src/main.c b/firmware/apps/_samples/audio/src/main.c index 3a9588a..73f3fc1 100644 --- a/firmware/apps/_samples/audio/src/main.c +++ b/firmware/apps/_samples/audio/src/main.c @@ -1,14 +1,19 @@ #include #include +#include #include #include #include +#include LOG_MODULE_REGISTER(MMS, LOG_LEVEL_INF); - int main(void) { + LOG_INF("Starting Audio Sample Application..."); + LOG_INF("Sleeping for one second to allow thread analyzer to initialize and log thread states before we start the test."); + k_sleep(K_MSEC(1000)); + int err; LOG_INF("Audio test snippet"); err = fs_mgmt_init(); @@ -18,7 +23,6 @@ int main(void) return err; } - k_sleep(K_MSEC(100)); // Give some time for the filesystem to initialize err = audio_init(); if (err) { @@ -26,6 +30,20 @@ int main(void) return err; } + LOG_INF("Triggering NULL file playback to test error handling..."); + audio_play_file(NULL); + while(log_process()); + LOG_INF("Triggering NULL sound to test error handling..."); + audio_play_sound(NULL); + while(log_process()); + LOG_INF("Triggering nonexistent sound to test error handling..."); + audio_play_sound("nonexistent_file"); + k_sleep(K_MSEC(100)); + while(log_process()); + LOG_INF("Triggering very long file name to test error handling..."); + audio_play_sound("very_long_file_name_that_exceeds_the_maximum_length_allowed_by_the_system_to_test_error_handling"); + while(log_process()); + LOG_INF("Triggering first sound..."); audio_play_sound("s1"); k_sleep(K_MSEC(100)); @@ -34,10 +52,17 @@ int main(void) audio_play_sound("s1"); k_sleep(K_MSEC(100)); - // Directly stop the I2S peripheral to simulate an abrupt stop that might occur with a DMA failure or similar issue. This will cause the next playback attempt to hit the slab timeout and trigger the I2S reset logic in the audio thread. + // Directly stop the I2S peripheral to simulate an abrupt stop that + // might occur with a DMA failure or similar issue. This will cause the + // next playback attempt to hit the slab timeout and trigger the I2S + // reset logic in the audio thread. + LOG_INF("Simulating failure by stopping I2S directly..."); NRF_I2S0->TASKS_STOP = 1; NRF_I2S0->ENABLE = 0; LOG_INF("Triggering third sound after failure simulation..."); audio_play_sound("s1"); + LOG_INF(FORMAT_GREEN_BOLD("If you made it to this point, the test completed successfully and everything should work fine!")); + LOG_INF(FORMAT_BRIGHT_BOLD("More output might follow due to the async nature of the audio playback.")); + return 0; } diff --git a/firmware/libs/audio/Kconfig b/firmware/libs/audio/Kconfig index d4c7f1a..9b5b534 100644 --- a/firmware/libs/audio/Kconfig +++ b/firmware/libs/audio/Kconfig @@ -21,7 +21,7 @@ if AUDIO help Set the audio sample rate in Hz. Common values are 8000, 16000, 44100, and 48000 Hz. Default is 16000 Hz. - config AUDIO_BIT_WIDTH + config AUDIO_WORD_WIDTH int "Audio Bit Depth" default 16 range 8 32 @@ -51,7 +51,7 @@ if AUDIO config AUDIO_STACK_SIZE int "Audio Thread Stack Size (bytes)" - default 2048 + default 1200 range 256 8192 help Set the stack size for the audio processing thread in bytes. Default is 2048 bytes. diff --git a/firmware/libs/audio/include/audio.h b/firmware/libs/audio/include/audio.h index cbdfa4e..a2599d1 100644 --- a/firmware/libs/audio/include/audio.h +++ b/firmware/libs/audio/include/audio.h @@ -1,3 +1,21 @@ +/** + * @file audio.h + * @brief Public API for the audio subsystem. + * This header defines the public interface for the audio subsystem, which + * provides functionality to play audio files and manage audio playback. It + * abstracts away the details of the underlying I2S peripheral and file + * system, allowing other parts of the application to easily trigger audio + * playback by specifying file paths or sound names. + * + * FLASH MEMORY USAGE: + * LOG LEVEL DEBUG: ~1.8 KB + * LOG LEVEL INFO: ~1.7 KB + * LOG LEVEL WARNING: ~1.2 KB + * LOG LEVEL ERROR: ~1.1 KB + * + * RAM USAGE (without stack and audio buffers): + * Any LOG LEVEL: ~0.47 KB + */ #ifndef AUDIO_H #define AUDIO_H diff --git a/firmware/libs/audio/src/audio.c b/firmware/libs/audio/src/audio.c index eea45cd..5a3106e 100644 --- a/firmware/libs/audio/src/audio.c +++ b/firmware/libs/audio/src/audio.c @@ -7,6 +7,10 @@ LOG_MODULE_REGISTER(audio, CONFIG_AUDIO_LOG_LEVEL); +#define SAMPLES_PER_BLOCK (CONFIG_AUDIO_BLOCK_SIZE / (CONFIG_AUDIO_WORD_WIDTH / 8) / 2) // Divide by 2 for stereo +#define BLOCK_DURATION_MS ((SAMPLES_PER_BLOCK * 1000U) / CONFIG_AUDIO_SAMPLE_RATE) // Duration of audio in each block in milliseconds +#define MAX_WAIT_TIME_MS (3 * BLOCK_DURATION_MS) // Maximum time to wait for the I2S peripheral to request the next block before we consider it stalled and reset it + /* Get the I2S device from the devicetree */ #define I2S_NODE DT_NODELABEL(i2s0) static const struct device *i2s_dev = DEVICE_DT_GET(I2S_NODE); @@ -71,7 +75,7 @@ void audio_thread_fn(void *p1, void *p2, void *p3) while (!abort_playback) { void *mem_block; - if (k_mem_slab_alloc(&audio_slab, &mem_block, K_MSEC(100)) < 0) + if (k_mem_slab_alloc(&audio_slab, &mem_block, K_MSEC(MAX_WAIT_TIME_MS)) < 0) { LOG_ERR("audio: slab timeout (I2S stall? DMA failure?) - skipping sound and resetting I2S..."); i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP); @@ -185,7 +189,7 @@ int audio_init(void) /* Initial configuration of the I2S peripheral */ struct i2s_config config = { - .word_size = CONFIG_AUDIO_BIT_WIDTH, + .word_size = CONFIG_AUDIO_WORD_WIDTH, .channels = 2, .format = I2S_FMT_DATA_FORMAT_I2S, .options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER, @@ -202,7 +206,7 @@ int audio_init(void) return ret; } - LOG_DBG("I2S driver initialized and configured for %d Hz", CONFIG_AUDIO_SAMPLE_RATE); + LOG_DBG("Audio subsystem initialized successfully"); return 0; } @@ -217,7 +221,7 @@ int audio_play_file(const char *file) size_t len = strnlen(file, CONFIG_AUDIO_MAX_PATH_LEN); if (len >= CONFIG_AUDIO_MAX_PATH_LEN) { - LOG_ERR("audio_play_file: file path too long"); + LOG_ERR("audio_play_file: file path too long: %s", file); return -ENAMETOOLONG; } @@ -236,6 +240,18 @@ int audio_play_file(const char *file) int audio_play_sound(const char *file) { + if (file == NULL) + { + LOG_ERR("audio_play_sound: file name is NULL"); + return -EINVAL; + } + + size_t len = strnlen(file, CONFIG_AUDIO_MAX_PATH_LEN) + strlen(CONFIG_FS_MGMT_MOUNT_POINT) + strlen(CONFIG_AUDIO_SAMPLE_FOLDER) + 2; + if (len >= CONFIG_AUDIO_MAX_PATH_LEN) + { + LOG_ERR("audio_play_sound: file path too long: %s/%s/%s", CONFIG_FS_MGMT_MOUNT_POINT, CONFIG_AUDIO_SAMPLE_FOLDER, file); + return -ENAMETOOLONG; + } char path[CONFIG_AUDIO_MAX_PATH_LEN]; snprintf(path, sizeof(path), "%s/%s/%s", diff --git a/firmware/libs/lasertag_utils/include/lasertag_utils.h b/firmware/libs/lasertag_utils/include/lasertag_utils.h index 85e512a..2ffc025 100644 --- a/firmware/libs/lasertag_utils/include/lasertag_utils.h +++ b/firmware/libs/lasertag_utils/include/lasertag_utils.h @@ -44,4 +44,33 @@ int lasertag_init_watchdog(void); * @brief Feed the watchdog timer to prevent a system reset. */ void lasertag_feed_watchdog(void); -#endif /* LASERTAG_UTILS_H */ \ No newline at end of file +#endif /* LASERTAG_UTILS_H */ + +/** + * ANSI Defines for bold text formatting in logs. + */ +#define ANSI_RESET "\x1b[0m" + +#define ANSI_BOLD "\x1b[1m" + +#define ANSI_BRIGHT "\x1b[97m" +#define ANSI_GREEN "\x1b[32m" +#define ANSI_YELLOW "\x1b[33m" +#define ANSI_RED "\x1b[31m" +#define ANSI_BLUE "\x1b[34m" + +#define FORMAT_BOLD(s) ANSI_BOLD s ANSI_RESET + +#define FORMAT_BRIGHT(s) ANSI_BRIGHT s ANSI_RESET +#define FORMAT_BRIGHT_BOLD(s) ANSI_BRIGHT ANSI_BOLD s ANSI_RESET + +#define FORMAT_GREEN(s) ANSI_GREEN s ANSI_RESET +#define FORMAT_GREEN_BOLD(s) ANSI_GREEN ANSI_BOLD s ANSI_RESET +#define FORMAT_YELLOW(s) ANSI_YELLOW s ANSI_RESET +#define FORMAT_YELLOW_BOLD(s) ANSI_YELLOW ANSI_BOLD s ANSI_RESET +#define FORMAT_RED(s) ANSI_RED s ANSI_RESET +#define FORMAT_RED_BOLD(s) ANSI_RED ANSI_BOLD s ANSI_RESET +#define FORMAT_BLUE(s) ANSI_BLUE s ANSI_RESET +#define FORMAT_BLUE_BOLD(s) ANSI_BLUE ANSI_BOLD s ANSI_RESET + +