From 141ee920651961289e9f99fb39bda4f8c77071bd Mon Sep 17 00:00:00 2001 From: Eng1n33r Date: Wed, 1 Jun 2022 19:00:39 +0300 Subject: [PATCH] add wav player & music player icon --- applications/applications.c | 12 +- applications/applications.mk | 8 + applications/wav_player/wav_parser.c | 84 ++++++ applications/wav_player/wav_parser.h | 51 ++++ applications/wav_player/wav_player.c | 301 ++++++++++++++++++++++ applications/wav_player/wav_player_hal.c | 58 +++++ applications/wav_player/wav_player_hal.h | 23 ++ applications/wav_player/wav_player_view.c | 214 +++++++++++++++ applications/wav_player/wav_player_view.h | 45 ++++ assets/compiled/assets_icons.c | 7 + assets/compiled/assets_icons.h | 1 + 11 files changed, 803 insertions(+), 1 deletion(-) create mode 100644 applications/wav_player/wav_parser.c create mode 100644 applications/wav_player/wav_parser.h create mode 100644 applications/wav_player/wav_player.c create mode 100644 applications/wav_player/wav_player_hal.c create mode 100644 applications/wav_player/wav_player_hal.h create mode 100644 applications/wav_player/wav_player_view.c create mode 100644 applications/wav_player/wav_player_view.h diff --git a/applications/applications.c b/applications/applications.c index dccab589f..e3d4c77df 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -21,6 +21,7 @@ extern int32_t accessor_app(void* p); extern int32_t archive_app(void* p); extern int32_t bad_usb_app(void* p); extern int32_t u2f_app(void* p); +extern int32_t wav_player_app(void* p); extern int32_t uart_echo_app(void* p); extern int32_t blink_test_app(void* p); extern int32_t bt_debug_app(void* p); @@ -339,7 +340,7 @@ const FlipperApplication FLIPPER_PLUGINS[] = { {.app = music_player_app, .name = "Music Player", .stack_size = 2048, - .icon = &A_Plugins_14, + .icon = &A_MusicPlayer_14, .flags = FlipperApplicationFlagDefault}, #endif @@ -350,6 +351,15 @@ const FlipperApplication FLIPPER_PLUGINS[] = { .icon = &A_Plugins_14, .flags = FlipperApplicationFlagDefault}, #endif + +#ifdef APP_WAV_PLAYER + {.app = wav_player_app, + .name = "WAV Player", + .stack_size = 4096, + .icon = &A_MusicPlayer_14, + .flags = FlipperApplicationFlagDefault}, +#endif + }; const size_t FLIPPER_PLUGINS_COUNT = COUNT_OF(FLIPPER_PLUGINS); diff --git a/applications/applications.mk b/applications/applications.mk index e7a9ebfc0..aa97f6207 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -47,6 +47,7 @@ APP_UPDATER = 1 # Plugins APP_MUSIC_PLAYER = 1 APP_SNAKE_GAME = 1 +APP_WAV_PLAYER = 1 # Debug APP_ACCESSOR = 1 @@ -240,6 +241,13 @@ CFLAGS += -DAPP_SNAKE_GAME SRV_GUI = 1 endif +APP_WAV_PLAYER ?= 0 +ifeq ($(APP_WAV_PLAYER), 1) +CFLAGS += -DAPP_WAV_PLAYER +SRV_GUI = 1 +endif + + APP_IBUTTON ?= 0 ifeq ($(APP_IBUTTON), 1) CFLAGS += -DAPP_IBUTTON diff --git a/applications/wav_player/wav_parser.c b/applications/wav_player/wav_parser.c new file mode 100644 index 000000000..c2897706c --- /dev/null +++ b/applications/wav_player/wav_parser.c @@ -0,0 +1,84 @@ +#include "wav_parser.h" + +#define TAG "WavParser" + +const char* format_text(FormatTag tag) { + switch(tag) { + case FormatTagPCM: + return "PCM"; + case FormatTagIEEE_FLOAT: + return "IEEE FLOAT"; + default: + return "Unknown"; + } +}; + +struct WavParser { + WavHeaderChunk header; + WavFormatChunk format; + WavDataChunk data; + size_t wav_data_start; + size_t wav_data_end; +}; + +WavParser* wav_parser_alloc() { + return malloc(sizeof(WavParser)); +} + +void wav_parser_free(WavParser* parser) { + free(parser); +} + +bool wav_parser_parse(WavParser* parser, Stream* stream) { + stream_read(stream, (uint8_t*)&parser->header, sizeof(WavHeaderChunk)); + stream_read(stream, (uint8_t*)&parser->format, sizeof(WavFormatChunk)); + stream_read(stream, (uint8_t*)&parser->data, sizeof(WavDataChunk)); + + if(memcmp(parser->header.riff, "RIFF", 4) != 0 || + memcmp(parser->header.wave, "WAVE", 4) != 0) { + FURI_LOG_E(TAG, "WAV: wrong header"); + return false; + } + + if(memcmp(parser->format.fmt, "fmt ", 4) != 0) { + FURI_LOG_E(TAG, "WAV: wrong format"); + return false; + } + + if(parser->format.tag != FormatTagPCM || memcmp(parser->data.data, "data", 4) != 0) { + FURI_LOG_E( + TAG, + "WAV: non-PCM format %u, next '%lu'", + parser->format.tag, + (uint32_t)parser->data.data); + return false; + } + + FURI_LOG_I( + TAG, + "Format tag: %s, ch: %u, smplrate: %lu, bps: %lu, bits: %u", + format_text(parser->format.tag), + parser->format.channels, + parser->format.sample_rate, + parser->format.byte_per_sec, + parser->format.bits_per_sample); + + parser->wav_data_start = stream_tell(stream); + parser->wav_data_end = parser->wav_data_start + parser->data.size; + + FURI_LOG_I(TAG, "data: %u - %u", parser->wav_data_start, parser->wav_data_end); + + return true; +} + +size_t wav_parser_get_data_start(WavParser* parser) { + return parser->wav_data_start; +} + +size_t wav_parser_get_data_end(WavParser* parser) { + return parser->wav_data_end; +} + +size_t wav_parser_get_data_len(WavParser* parser) { + return parser->wav_data_end - parser->wav_data_start; +} diff --git a/applications/wav_player/wav_parser.h b/applications/wav_player/wav_parser.h new file mode 100644 index 000000000..f50c48b3f --- /dev/null +++ b/applications/wav_player/wav_parser.h @@ -0,0 +1,51 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FormatTagPCM = 0x0001, + FormatTagIEEE_FLOAT = 0x0003, +} FormatTag; + +typedef struct { + uint8_t riff[4]; + uint32_t size; + uint8_t wave[4]; +} WavHeaderChunk; + +typedef struct { + uint8_t fmt[4]; + uint32_t size; + uint16_t tag; + uint16_t channels; + uint32_t sample_rate; + uint32_t byte_per_sec; + uint16_t block_align; + uint16_t bits_per_sample; +} WavFormatChunk; + +typedef struct { + uint8_t data[4]; + uint32_t size; +} WavDataChunk; + +typedef struct WavParser WavParser; + +WavParser* wav_parser_alloc(); + +void wav_parser_free(WavParser* parser); + +bool wav_parser_parse(WavParser* parser, Stream* stream); + +size_t wav_parser_get_data_start(WavParser* parser); + +size_t wav_parser_get_data_end(WavParser* parser); + +size_t wav_parser_get_data_len(WavParser* parser); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/wav_player/wav_player.c b/applications/wav_player/wav_player.c new file mode 100644 index 000000000..8c88f8004 --- /dev/null +++ b/applications/wav_player/wav_player.c @@ -0,0 +1,301 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wav_player_hal.h" +#include "wav_parser.h" +#include "wav_player_view.h" + +#define TAG "WavPlayer" + +static bool open_wav_stream(Storage* storage, Stream* stream) { + DialogsApp* dialogs = furi_record_open("dialogs"); + bool result = false; + string_t path; + string_init(path); + string_set_str(path, "/ext/wav_player"); + bool ret = + dialog_file_browser_show(dialogs, path, path, ".wav", + true, + &I_music_10px, + false); + + furi_record_close("dialogs"); + if(ret) { + if(!file_stream_open(stream, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Cannot open file \"%s\"", string_get_cstr(path)); + } else { + result = true; + } + } + string_clear(path); + return result; +} + +typedef enum { + WavPlayerEventHalfTransfer, + WavPlayerEventFullTransfer, + WavPlayerEventCtrlVolUp, + WavPlayerEventCtrlVolDn, + WavPlayerEventCtrlMoveL, + WavPlayerEventCtrlMoveR, + WavPlayerEventCtrlOk, + WavPlayerEventCtrlBack, +} WavPlayerEventType; + +typedef struct { + WavPlayerEventType type; +} WavPlayerEvent; + +static void wav_player_dma_isr(void* ctx) { + osMessageQueueId_t event_queue = ctx; + + // half of transfer + if(LL_DMA_IsActiveFlag_HT1(DMA1)) { + LL_DMA_ClearFlag_HT1(DMA1); + // fill first half of buffer + WavPlayerEvent event = {.type = WavPlayerEventHalfTransfer}; + osMessageQueuePut(event_queue, &event, 0, 0); + } + + // transfer complete + if(LL_DMA_IsActiveFlag_TC1(DMA1)) { + LL_DMA_ClearFlag_TC1(DMA1); + // fill second half of buffer + WavPlayerEvent event = {.type = WavPlayerEventFullTransfer}; + osMessageQueuePut(event_queue, &event, 0, 0); + } +} + +typedef struct { + Storage* storage; + Stream* stream; + WavParser* parser; + uint16_t* sample_buffer; + uint8_t* tmp_buffer; + + size_t samples_count_half; + size_t samples_count; + + osMessageQueueId_t queue; + + float volume; + bool play; + + WavPlayerView* view; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notification; +} WavPlayerApp; + +static WavPlayerApp* app_alloc() { + WavPlayerApp* app = malloc(sizeof(WavPlayerApp)); + app->samples_count_half = 1024 * 4; + app->samples_count = app->samples_count_half * 2; + app->storage = furi_record_open("storage"); + app->stream = file_stream_alloc(app->storage); + app->parser = wav_parser_alloc(); + app->sample_buffer = malloc(sizeof(uint16_t) * app->samples_count); + app->tmp_buffer = malloc(sizeof(uint8_t) * app->samples_count); + app->queue = osMessageQueueNew(10, sizeof(WavPlayerEvent), NULL); + + app->volume = 10.0f; + app->play = true; + + app->gui = furi_record_open("gui"); + app->view_dispatcher = view_dispatcher_alloc(); + app->view = wav_player_view_alloc(); + + view_dispatcher_add_view(app->view_dispatcher, 0, wav_player_view_get_view(app->view)); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_switch_to_view(app->view_dispatcher, 0); + + app->notification = furi_record_open("notification"); + notification_message(app->notification, &sequence_display_backlight_enforce_on); + + return app; +} + +static void app_free(WavPlayerApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, 0); + view_dispatcher_free(app->view_dispatcher); + wav_player_view_free(app->view); + furi_record_close("gui"); + + osMessageQueueDelete(app->queue); + free(app->tmp_buffer); + free(app->sample_buffer); + wav_parser_free(app->parser); + stream_free(app->stream); + furi_record_close("storage"); + + notification_message(app->notification, &sequence_display_backlight_enforce_auto); + furi_record_close("notification"); + free(app); +} + +// TODO: that works only with 8-bit 2ch audio +static bool fill_data(WavPlayerApp* app, size_t index) { + uint16_t* sample_buffer_start = &app->sample_buffer[index]; + size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count); + + for(size_t i = count; i < app->samples_count; i++) { + app->tmp_buffer[i] = 0; + } + + for(size_t i = 0; i < app->samples_count; i += 2) { + float data = app->tmp_buffer[i]; + data -= UINT8_MAX / 2; // to signed + data /= UINT8_MAX / 2; // scale -1..1 + + data *= app->volume; // volume + data = tanhf(data); // hyperbolic tangent limiter + + data *= UINT8_MAX / 2; // scale -128..127 + data += UINT8_MAX / 2; // to unsigned + + if(data < 0) { + data = 0; + } + + if(data > 255) { + data = 255; + } + + sample_buffer_start[i / 2] = data; + } + + wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half); + + return count != app->samples_count; +} + +static void ctrl_callback(WavPlayerCtrl ctrl, void* ctx) { + osMessageQueueId_t event_queue = ctx; + WavPlayerEvent event; + + switch(ctrl) { + case WavPlayerCtrlVolUp: + event.type = WavPlayerEventCtrlVolUp; + osMessageQueuePut(event_queue, &event, 0, 0); + break; + case WavPlayerCtrlVolDn: + event.type = WavPlayerEventCtrlVolDn; + osMessageQueuePut(event_queue, &event, 0, 0); + break; + case WavPlayerCtrlMoveL: + event.type = WavPlayerEventCtrlMoveL; + osMessageQueuePut(event_queue, &event, 0, 0); + break; + case WavPlayerCtrlMoveR: + event.type = WavPlayerEventCtrlMoveR; + osMessageQueuePut(event_queue, &event, 0, 0); + break; + case WavPlayerCtrlOk: + event.type = WavPlayerEventCtrlOk; + osMessageQueuePut(event_queue, &event, 0, 0); + break; + case WavPlayerCtrlBack: + event.type = WavPlayerEventCtrlBack; + osMessageQueuePut(event_queue, &event, 0, 0); + break; + default: + break; + } +} + +static void app_run(WavPlayerApp* app) { + if(!open_wav_stream(app->storage, app->stream)) return; + if(!wav_parser_parse(app->parser, app->stream)) return; + + wav_player_view_set_volume(app->view, app->volume); + wav_player_view_set_start(app->view, wav_parser_get_data_start(app->parser)); + wav_player_view_set_current(app->view, stream_tell(app->stream)); + wav_player_view_set_end(app->view, wav_parser_get_data_end(app->parser)); + wav_player_view_set_play(app->view, app->play); + + wav_player_view_set_context(app->view, app->queue); + wav_player_view_set_ctrl_callback(app->view, ctrl_callback); + + bool eof = fill_data(app, 0); + eof = fill_data(app, app->samples_count_half); + + wav_player_speaker_init(); + wav_player_dma_init((uint32_t)app->sample_buffer, app->samples_count); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, wav_player_dma_isr, app->queue); + + wav_player_dma_start(); + wav_player_speaker_start(); + + WavPlayerEvent event; + + while(1) { + if(osMessageQueueGet(app->queue, &event, NULL, osWaitForever) == osOK) { + if(event.type == WavPlayerEventHalfTransfer) { + eof = fill_data(app, 0); + wav_player_view_set_current(app->view, stream_tell(app->stream)); + if(eof) { + stream_seek( + app->stream, + wav_parser_get_data_start(app->parser), + StreamOffsetFromStart); + } + + } else if(event.type == WavPlayerEventFullTransfer) { + eof = fill_data(app, app->samples_count_half); + wav_player_view_set_current(app->view, stream_tell(app->stream)); + if(eof) { + stream_seek( + app->stream, + wav_parser_get_data_start(app->parser), + StreamOffsetFromStart); + } + } else if(event.type == WavPlayerEventCtrlVolUp) { + if(app->volume < 9.9) app->volume += 0.2; + wav_player_view_set_volume(app->view, app->volume); + } else if(event.type == WavPlayerEventCtrlVolDn) { + if(app->volume > 0.01) app->volume -= 0.2; + wav_player_view_set_volume(app->view, app->volume); + } else if(event.type == WavPlayerEventCtrlMoveL) { + int32_t seek = stream_tell(app->stream) - wav_parser_get_data_start(app->parser); + seek = MIN(seek, wav_parser_get_data_len(app->parser) / 100); + stream_seek(app->stream, -seek, StreamOffsetFromCurrent); + wav_player_view_set_current(app->view, stream_tell(app->stream)); + } else if(event.type == WavPlayerEventCtrlMoveR) { + int32_t seek = wav_parser_get_data_end(app->parser) - stream_tell(app->stream); + seek = MIN(seek, wav_parser_get_data_len(app->parser) / 100); + stream_seek(app->stream, seek, StreamOffsetFromCurrent); + wav_player_view_set_current(app->view, stream_tell(app->stream)); + } else if(event.type == WavPlayerEventCtrlOk) { + app->play = !app->play; + wav_player_view_set_play(app->view, app->play); + + if(!app->play) { + wav_player_speaker_stop(); + } else { + wav_player_speaker_start(); + } + } else if(event.type == WavPlayerEventCtrlBack) { + break; + } + } + } + + wav_player_speaker_stop(); + wav_player_dma_stop(); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); +} + +int32_t wav_player_app(void* p) { + WavPlayerApp* app = app_alloc(); + app_run(app); + app_free(app); + return 0; +} diff --git a/applications/wav_player/wav_player_hal.c b/applications/wav_player/wav_player_hal.c new file mode 100644 index 000000000..ad0c019be --- /dev/null +++ b/applications/wav_player/wav_player_hal.c @@ -0,0 +1,58 @@ +#include "wav_player_hal.h" +#include +#include + +#define FURI_HAL_SPEAKER_TIMER TIM16 +#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 +#define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1 + +void wav_player_speaker_init() { + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = 4; + TIM_InitStruct.Autoreload = 255; + LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 127; + LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); +} + +void wav_player_speaker_start() { + LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); +} + +void wav_player_speaker_stop() { + LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); +} + +void wav_player_dma_init(uint32_t address, size_t size) { + uint32_t dma_dst = (uint32_t) & (FURI_HAL_SPEAKER_TIMER->CCR1); + + LL_DMA_ConfigAddresses(DMA_INSTANCE, address, dma_dst, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); + LL_DMA_SetDataLength(DMA_INSTANCE, size); + + LL_DMA_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM16_UP); + LL_DMA_SetDataTransferDirection(DMA_INSTANCE, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); + LL_DMA_SetChannelPriorityLevel(DMA_INSTANCE, LL_DMA_PRIORITY_VERYHIGH); + LL_DMA_SetMode(DMA_INSTANCE, LL_DMA_MODE_CIRCULAR); + LL_DMA_SetPeriphIncMode(DMA_INSTANCE, LL_DMA_PERIPH_NOINCREMENT); + LL_DMA_SetMemoryIncMode(DMA_INSTANCE, LL_DMA_MEMORY_INCREMENT); + LL_DMA_SetPeriphSize(DMA_INSTANCE, LL_DMA_PDATAALIGN_HALFWORD); + LL_DMA_SetMemorySize(DMA_INSTANCE, LL_DMA_MDATAALIGN_HALFWORD); + + LL_DMA_EnableIT_TC(DMA_INSTANCE); + LL_DMA_EnableIT_HT(DMA_INSTANCE); +} + +void wav_player_dma_start() { + LL_DMA_EnableChannel(DMA_INSTANCE); + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_SPEAKER_TIMER); +} + +void wav_player_dma_stop() { + LL_DMA_DisableChannel(DMA_INSTANCE); +} \ No newline at end of file diff --git a/applications/wav_player/wav_player_hal.h b/applications/wav_player/wav_player_hal.h new file mode 100644 index 000000000..124f51406 --- /dev/null +++ b/applications/wav_player/wav_player_hal.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void wav_player_speaker_init(); + +void wav_player_speaker_start(); + +void wav_player_speaker_stop(); + +void wav_player_dma_init(uint32_t address, size_t size); + +void wav_player_dma_start(); + +void wav_player_dma_stop(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/wav_player/wav_player_view.c b/applications/wav_player/wav_player_view.c new file mode 100644 index 000000000..8068bf351 --- /dev/null +++ b/applications/wav_player/wav_player_view.c @@ -0,0 +1,214 @@ +#include "wav_player_view.h" + +#define DATA_COUNT 116 + +struct WavPlayerView { + View* view; + WavPlayerCtrlCallback callback; + void* context; +}; + +typedef struct { + bool play; + float volume; + size_t start; + size_t end; + size_t current; + uint8_t data[DATA_COUNT]; +} WavPlayerViewModel; + +float map(float x, float in_min, float in_max, float out_min, float out_max) { + return (x - in_min) * (out_max - out_min + 1) / (in_max - in_min + 1) + out_min; +} + +static void wav_player_view_draw_callback(Canvas* canvas, void* _model) { + WavPlayerViewModel* model = _model; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + uint8_t x_pos = 0; + uint8_t y_pos = 0; + + // volume + x_pos = 124; + y_pos = 0; + const float volume = (64 / 10.0f) * model->volume; + canvas_draw_frame(canvas, x_pos, y_pos, 4, 64); + canvas_draw_box(canvas, x_pos, y_pos + (64 - volume), 4, volume); + + // play / pause + x_pos = 58; + y_pos = 55; + if(model->play) { + canvas_draw_line(canvas, x_pos, y_pos, x_pos + 8, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos + 8, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos, y_pos); + } else { + canvas_draw_box(canvas, x_pos, y_pos, 3, 9); + canvas_draw_box(canvas, x_pos + 4, y_pos, 3, 9); + } + + x_pos = 78; + y_pos = 55; + canvas_draw_line(canvas, x_pos, y_pos, x_pos + 4, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos + 4, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos, y_pos); + + x_pos = 82; + y_pos = 55; + canvas_draw_line(canvas, x_pos, y_pos, x_pos + 4, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos + 4, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos, y_pos); + + x_pos = 40; + y_pos = 55; + canvas_draw_line(canvas, x_pos, y_pos, x_pos - 4, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos - 4, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos, y_pos); + + x_pos = 44; + y_pos = 55; + canvas_draw_line(canvas, x_pos, y_pos, x_pos - 4, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos - 4, y_pos + 4); + canvas_draw_line(canvas, x_pos, y_pos + 8, x_pos, y_pos); + + // len + x_pos = 4; + y_pos = 47; + const uint8_t play_len = 116; + uint8_t play_pos = map(model->current, model->start, model->end, 0, play_len - 4); + + canvas_draw_frame(canvas, x_pos, y_pos, play_len, 4); + canvas_draw_box(canvas, x_pos + play_pos, y_pos - 2, 4, 8); + canvas_draw_box(canvas, x_pos, y_pos, play_pos, 4); + + // osc + x_pos = 4; + y_pos = 0; + for(size_t i = 1; i < DATA_COUNT; i++) { + canvas_draw_line(canvas, x_pos + i - 1, model->data[i - 1], x_pos + i, model->data[i]); + } +} + +static bool wav_player_view_input_callback(InputEvent* event, void* context) { + WavPlayerView* wav_player_view = context; + bool consumed = false; + + if(wav_player_view->callback) { + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + if(event->key == InputKeyUp) { + wav_player_view->callback(WavPlayerCtrlVolUp, wav_player_view->context); + consumed = true; + } else if(event->key == InputKeyDown) { + wav_player_view->callback(WavPlayerCtrlVolDn, wav_player_view->context); + consumed = true; + } else if(event->key == InputKeyLeft) { + wav_player_view->callback(WavPlayerCtrlMoveL, wav_player_view->context); + consumed = true; + } else if(event->key == InputKeyRight) { + wav_player_view->callback(WavPlayerCtrlMoveR, wav_player_view->context); + consumed = true; + } else if(event->key == InputKeyOk) { + wav_player_view->callback(WavPlayerCtrlOk, wav_player_view->context); + consumed = true; + } else if(event->key == InputKeyBack) { + wav_player_view->callback(WavPlayerCtrlBack, wav_player_view->context); + consumed = true; + } + } + } + + return consumed; +} + +WavPlayerView* wav_player_view_alloc() { + WavPlayerView* wav_view = malloc(sizeof(WavPlayerView)); + wav_view->view = view_alloc(); + view_set_context(wav_view->view, wav_view); + view_allocate_model(wav_view->view, ViewModelTypeLocking, sizeof(WavPlayerViewModel)); + view_set_draw_callback(wav_view->view, wav_player_view_draw_callback); + view_set_input_callback(wav_view->view, wav_player_view_input_callback); + + return wav_view; +} + +void wav_player_view_free(WavPlayerView* wav_view) { + furi_assert(wav_view); + view_free(wav_view->view); + free(wav_view); +} + +View* wav_player_view_get_view(WavPlayerView* wav_view) { + furi_assert(wav_view); + return wav_view->view; +} + +void wav_player_view_set_volume(WavPlayerView* wav_view, float volume) { + furi_assert(wav_view); + with_view_model( + wav_view->view, (WavPlayerViewModel * model) { + model->volume = volume; + return true; + }); +} + +void wav_player_view_set_start(WavPlayerView* wav_view, size_t start) { + furi_assert(wav_view); + with_view_model( + wav_view->view, (WavPlayerViewModel * model) { + model->start = start; + return true; + }); +} + +void wav_player_view_set_end(WavPlayerView* wav_view, size_t end) { + furi_assert(wav_view); + with_view_model( + wav_view->view, (WavPlayerViewModel * model) { + model->end = end; + return true; + }); +} + +void wav_player_view_set_current(WavPlayerView* wav_view, size_t current) { + furi_assert(wav_view); + with_view_model( + wav_view->view, (WavPlayerViewModel * model) { + model->current = current; + return true; + }); +} + +void wav_player_view_set_play(WavPlayerView* wav_view, bool play) { + furi_assert(wav_view); + with_view_model( + wav_view->view, (WavPlayerViewModel * model) { + model->play = play; + return true; + }); +} + +void wav_player_view_set_data(WavPlayerView* wav_view, uint16_t* data, size_t data_count) { + furi_assert(wav_view); + with_view_model( + wav_view->view, (WavPlayerViewModel * model) { + size_t inc = (data_count / DATA_COUNT) - 1; + + for(size_t i = 0; i < DATA_COUNT; i++) { + model->data[i] = *data / 6; + if(model->data[i] > 42) model->data[i] = 42; + data += inc; + } + return true; + }); +} + +void wav_player_view_set_ctrl_callback(WavPlayerView* wav_view, WavPlayerCtrlCallback callback) { + furi_assert(wav_view); + wav_view->callback = callback; +} + +void wav_player_view_set_context(WavPlayerView* wav_view, void* context) { + furi_assert(wav_view); + wav_view->context = context; +} \ No newline at end of file diff --git a/applications/wav_player/wav_player_view.h b/applications/wav_player/wav_player_view.h new file mode 100644 index 000000000..246aeaf3e --- /dev/null +++ b/applications/wav_player/wav_player_view.h @@ -0,0 +1,45 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct WavPlayerView WavPlayerView; + +typedef enum { + WavPlayerCtrlVolUp, + WavPlayerCtrlVolDn, + WavPlayerCtrlMoveL, + WavPlayerCtrlMoveR, + WavPlayerCtrlOk, + WavPlayerCtrlBack, +} WavPlayerCtrl; + +typedef void (*WavPlayerCtrlCallback)(WavPlayerCtrl ctrl, void* context); + +WavPlayerView* wav_player_view_alloc(); + +void wav_player_view_free(WavPlayerView* wav_view); + +View* wav_player_view_get_view(WavPlayerView* wav_view); + +void wav_player_view_set_volume(WavPlayerView* wav_view, float volume); + +void wav_player_view_set_start(WavPlayerView* wav_view, size_t start); + +void wav_player_view_set_end(WavPlayerView* wav_view, size_t end); + +void wav_player_view_set_current(WavPlayerView* wav_view, size_t current); + +void wav_player_view_set_play(WavPlayerView* wav_view, bool play); + +void wav_player_view_set_data(WavPlayerView* wav_view, uint16_t* data, size_t data_count); + +void wav_player_view_set_ctrl_callback(WavPlayerView* wav_view, WavPlayerCtrlCallback callback); + +void wav_player_view_set_context(WavPlayerView* wav_view, void* context); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/assets/compiled/assets_icons.c b/assets/compiled/assets_icons.c index fb125095a..f62745b77 100644 --- a/assets/compiled/assets_icons.c +++ b/assets/compiled/assets_icons.c @@ -372,6 +372,13 @@ const uint8_t _A_Infrared_14_4[] = {0x01,0x00,0x0e,0x00,0x00,0x5f,0x82,0x02,0x05 const uint8_t _A_Infrared_14_5[] = {0x01,0x00,0x15,0x00,0x00,0x2f,0xc2,0x07,0x08,0x82,0x01,0x47,0xc1,0x01,0x05,0x98,0x14,0x41,0xa3,0xf8,0x83,0x80,0x47,0xff,0x3f,}; const uint8_t* const _A_Infrared_14[] = {_A_Infrared_14_0,_A_Infrared_14_1,_A_Infrared_14_2,_A_Infrared_14_3,_A_Infrared_14_4,_A_Infrared_14_5}; +const uint8_t _A_MusicPlayer_14_0[] = {0x01,0x00,0x17,0x00,0x00,0x1e,0x02,0x01,0xc0,0x80,0xf0,0x20,0x74,0x08,0x15,0x02,0x00,0x01,0x3b,0x84,0x02,0xf0,0x01,0x29,0x80,0x5c,0x80,}; +const uint8_t _A_MusicPlayer_14_1[] = {0x01,0x00,0x16,0x00,0x80,0x41,0x20,0x10,0xe8,0x04,0x7a,0x01,0x12,0x80,0x40,0x80,0x27,0x80,0x81,0xf0,0x00,0x25,0x80,0x80,0x86,0x94,}; +const uint8_t _A_MusicPlayer_14_2[] = {0x01,0x00,0x13,0x00,0x00,0x34,0x82,0x03,0x30,0x81,0xcc,0x20,0x51,0x08,0x00,0x05,0x63,0x90,0x08,0xf0,0x04,0xa1,0x80,}; +const uint8_t _A_MusicPlayer_14_3[] = {0x01,0x00,0x16,0x00,0x82,0x40,0x21,0xd0,0x08,0xf4,0x02,0x25,0x00,0x81,0x00,0x52,0x07,0x20,0x81,0xcc,0x00,0x23,0x01,0x90,0x06,0xd4,}; +const uint8_t _A_MusicPlayer_14_4[] = {0x01,0x00,0x15,0x00,0x00,0x2c,0x82,0x01,0x70,0x80,0x7c,0x20,0x19,0x08,0x04,0x40,0x02,0x91,0xc8,0x04,0x78,0x02,0x50,0xc8,0x00,}; +const uint8_t* const _A_MusicPlayer_14[] = {_A_MusicPlayer_14_0,_A_MusicPlayer_14_1,_A_MusicPlayer_14_2,_A_MusicPlayer_14_3,_A_MusicPlayer_14_4}; + const uint8_t _A_NFC_14_0[] = {0x00,0x00,0x08,0x00,0x10,0x00,0x12,0x00,0x22,0x42,0x24,0x87,0x24,0x8D,0x24,0x99,0x24,0xF1,0x24,0x62,0x24,0x00,0x22,0x00,0x12,0x00,0x10,0x00,0x08,}; const uint8_t _A_NFC_14_1[] = {0x01,0x00,0x1a,0x00,0x80,0x42,0x20,0x11,0x00,0x09,0x48,0x28,0x52,0x0c,0x3c,0x83,0x1b,0x20,0xcc,0xc8,0x3e,0x32,0x0b,0x14,0x80,0x1a,0x21,0x34,0x84,0x00,}; const uint8_t _A_NFC_14_2[] = {0x01,0x00,0x10,0x00,0x00,0x3d,0x0a,0x01,0x87,0x80,0x63,0x60,0x19,0x98,0x07,0xc6,0x01,0x62,0x09,0xc0,}; diff --git a/assets/compiled/assets_icons.h b/assets/compiled/assets_icons.h index 459478802..3d8a6fc9b 100644 --- a/assets/compiled/assets_icons.h +++ b/assets/compiled/assets_icons.h @@ -99,6 +99,7 @@ extern const Icon A_FileManager_14; extern const Icon A_GPIO_14; extern const Icon A_Games_14; extern const Icon A_Infrared_14; +extern const Icon A_MusicPlayer_14; extern const Icon A_NFC_14; extern const Icon A_Passport_14; extern const Icon A_Plugins_14;