From 06501789468959667c8db39be6d823d8bfe13527 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 18 Jan 2023 21:17:53 +0300 Subject: [PATCH] Fixed and improved WAV Player by LTVA1 https://github.com/LTVA1/wav_player --- ReadMe.md | 2 +- applications/plugins/wav_player/README.md | 4 + applications/plugins/wav_player/wav_parser.c | 6 +- applications/plugins/wav_player/wav_parser.h | 40 +++++- applications/plugins/wav_player/wav_player.c | 126 +++++++++++------- .../plugins/wav_player/wav_player_hal.c | 103 +++++++++----- .../plugins/wav_player/wav_player_view.c | 19 +-- .../plugins/wav_player/wav_player_view.h | 31 +++++ 8 files changed, 229 insertions(+), 102 deletions(-) create mode 100644 applications/plugins/wav_player/README.md diff --git a/ReadMe.md b/ReadMe.md index a4913ae2d..9111b3691 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -106,7 +106,7 @@ You can support us by using links or addresses below: - WiFi Scanner plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module) - MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff) - USB Keyboard plugin [(by huuck)](https://github.com/huuck/FlipperZeroUSBKeyboard) -- WAV player plugin (fixed) [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) +- WAV Player [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) - Fixed and improved by [LTVA1](https://github.com/LTVA1/wav_player) - Barcode generator plugin [(original by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) - [EAN-8 and refactoring](https://github.com/DarkFlippers/unleashed-firmware/pull/154) by @msvsergey - GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin) - ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) diff --git a/applications/plugins/wav_player/README.md b/applications/plugins/wav_player/README.md new file mode 100644 index 000000000..e5ea73c2f --- /dev/null +++ b/applications/plugins/wav_player/README.md @@ -0,0 +1,4 @@ +# WAV player + A Flipper Zero application for playing wav files. My fork adds support for correct playback speed (for files with different sample rates) and for mono files (original wav player only plays stereo). You still need to convert your file to unsigned 8-bit PCM format for it to played correctly on flipper. + +Original app by https://github.com/DrZlo13. \ No newline at end of file diff --git a/applications/plugins/wav_player/wav_parser.c b/applications/plugins/wav_player/wav_parser.c index c2897706c..1f534bacb 100644 --- a/applications/plugins/wav_player/wav_parser.c +++ b/applications/plugins/wav_player/wav_parser.c @@ -29,7 +29,7 @@ void wav_parser_free(WavParser* parser) { free(parser); } -bool wav_parser_parse(WavParser* parser, Stream* stream) { +bool wav_parser_parse(WavParser* parser, Stream* stream, WavPlayerApp* app) { 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)); @@ -63,6 +63,10 @@ bool wav_parser_parse(WavParser* parser, Stream* stream) { parser->format.byte_per_sec, parser->format.bits_per_sample); + app->sample_rate = parser->format.sample_rate; + app->num_channels = parser->format.channels; + app->bits_per_sample = parser->format.bits_per_sample; + parser->wav_data_start = stream_tell(stream); parser->wav_data_end = parser->wav_data_start + parser->data.size; diff --git a/applications/plugins/wav_player/wav_parser.h b/applications/plugins/wav_player/wav_parser.h index f50c48b3f..b442406f9 100644 --- a/applications/plugins/wav_player/wav_parser.h +++ b/applications/plugins/wav_player/wav_parser.h @@ -1,6 +1,18 @@ #pragma once #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wav_player_view.h" + #ifdef __cplusplus extern "C" { #endif @@ -34,11 +46,37 @@ typedef struct { typedef struct WavParser WavParser; +typedef struct { + Storage* storage; + Stream* stream; + WavParser* parser; + uint16_t* sample_buffer; + uint8_t* tmp_buffer; + + uint32_t sample_rate; + + uint16_t num_channels; + uint16_t bits_per_sample; + + size_t samples_count_half; + size_t samples_count; + + FuriMessageQueue* queue; + + float volume; + bool play; + + WavPlayerView* view; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notification; +} WavPlayerApp; + WavParser* wav_parser_alloc(); void wav_parser_free(WavParser* parser); -bool wav_parser_parse(WavParser* parser, Stream* stream); +bool wav_parser_parse(WavParser* parser, Stream* stream, WavPlayerApp* app); size_t wav_parser_get_data_start(WavParser* parser); diff --git a/applications/plugins/wav_player/wav_player.c b/applications/plugins/wav_player/wav_player.c index 1e79fce61..3fb8b1ea5 100644 --- a/applications/plugins/wav_player/wav_player.c +++ b/applications/plugins/wav_player/wav_player.c @@ -19,7 +19,7 @@ #define WAVPLAYER_FOLDER "/ext/wav_player" static bool open_wav_stream(Stream* stream) { - DialogsApp* dialogs = furi_record_open("dialogs"); + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); bool result = false; FuriString* path; path = furi_string_alloc(); @@ -32,7 +32,7 @@ static bool open_wav_stream(Stream* stream) { bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options); - furi_record_close("dialogs"); + furi_record_close(RECORD_DIALOGS); if(ret) { if(!file_stream_open(stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(path)); @@ -79,27 +79,6 @@ static void wav_player_dma_isr(void* ctx) { } } -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; - - FuriMessageQueue* 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; @@ -122,7 +101,7 @@ static WavPlayerApp* app_alloc() { 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"); + app->notification = furi_record_open(RECORD_NOTIFICATION); notification_message(app->notification, &sequence_display_backlight_enforce_on); return app; @@ -142,44 +121,89 @@ static void app_free(WavPlayerApp* app) { furi_record_close(RECORD_STORAGE); notification_message(app->notification, &sequence_display_backlight_enforce_auto); - furi_record_close("notification"); + furi_record_close(RECORD_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); + if(app->num_channels == 1) { + uint16_t* sample_buffer_start = &app->sample_buffer[index]; + size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count_half); - 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; + for(size_t i = count; i < app->samples_count_half; i++) { + app->tmp_buffer[i] = 0; } - if(data > 255) { - data = 255; + //for(size_t i = 0; i < app->samples_count; i += 2) + for(size_t i = 0; i < app->samples_count_half; i++) //now works with mono! + { + 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; + } + + //uint8_t data = app->tmp_buffer[i]; + + sample_buffer_start[i] = data; } - 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_half; } - wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half); + if(app->num_channels == 2) { + uint16_t* sample_buffer_start = &app->sample_buffer[index]; + size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count); - return count != 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] + app->tmp_buffer[i + 1]) / 2; // (L + R) / 2 + 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; + } + + //uint8_t data = app->tmp_buffer[i]; + + 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; + } + + return true; } static void ctrl_callback(WavPlayerCtrl ctrl, void* ctx) { @@ -218,7 +242,7 @@ static void ctrl_callback(WavPlayerCtrl ctrl, void* ctx) { static void app_run(WavPlayerApp* app) { if(!open_wav_stream(app->stream)) return; - if(!wav_parser_parse(app->parser, app->stream)) return; + if(!wav_parser_parse(app->parser, app->stream, app)) return; wav_player_view_set_volume(app->view, app->volume); wav_player_view_set_start(app->view, wav_parser_get_data_start(app->parser)); @@ -232,7 +256,7 @@ static void app_run(WavPlayerApp* app) { bool eof = fill_data(app, 0); eof = fill_data(app, app->samples_count_half); - wav_player_speaker_init(); + wav_player_speaker_init(app->sample_rate); wav_player_dma_init((uint32_t)app->sample_buffer, app->samples_count); furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, wav_player_dma_isr, app->queue); diff --git a/applications/plugins/wav_player/wav_player_hal.c b/applications/plugins/wav_player/wav_player_hal.c index ad0c019be..ece81fee0 100644 --- a/applications/plugins/wav_player/wav_player_hal.c +++ b/applications/plugins/wav_player/wav_player_hal.c @@ -2,57 +2,100 @@ #include #include +#include +#include +#include +#include + +//#define FURI_HAL_SPEAKER_TIMER TIM16 + #define FURI_HAL_SPEAKER_TIMER TIM16 + +#define SAMPLE_RATE_TIMER TIM2 + #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); +void wav_player_speaker_init(uint32_t sample_rate) +{ + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + //TIM_InitStruct.Prescaler = 4; + TIM_InitStruct.Prescaler = 1; + TIM_InitStruct.Autoreload = + 255; //in this fork used purely as PWM timer, the DMA now is triggered by SAMPLE_RATE_TIMER + 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); + 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); + + //====================================================== + + TIM_InitStruct.Prescaler = 0; + //TIM_InitStruct.Autoreload = 1451; //64 000 000 / 1451 ~= 44100 Hz + + TIM_InitStruct.Autoreload = 64000000 / sample_rate; //to support various sample rates + + LL_TIM_Init(SAMPLE_RATE_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(SAMPLE_RATE_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); + + //========================================================= + //configuring PA6 pin as TIM16 output + + //furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedVeryHigh, GpioAltFn14TIM16); + //furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedLow, GpioAltFn14TIM16); + furi_hal_gpio_init_ex(&gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedVeryHigh, GpioAltFn14TIM16); + //furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull); + //furi_hal_gpio_write(&gpio_ext_pa6, false); } void wav_player_speaker_start() { - LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); - LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); + LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); + + LL_TIM_EnableAllOutputs(SAMPLE_RATE_TIMER); + LL_TIM_EnableCounter(SAMPLE_RATE_TIMER); } void wav_player_speaker_stop() { - LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); - LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); + LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); + + LL_TIM_DisableAllOutputs(SAMPLE_RATE_TIMER); + LL_TIM_DisableCounter(SAMPLE_RATE_TIMER); } void wav_player_dma_init(uint32_t address, size_t size) { - uint32_t dma_dst = (uint32_t) & (FURI_HAL_SPEAKER_TIMER->CCR1); + 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_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_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM2_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); + 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); + LL_DMA_EnableChannel(DMA_INSTANCE); + LL_TIM_EnableDMAReq_UPDATE(SAMPLE_RATE_TIMER); } void wav_player_dma_stop() { - LL_DMA_DisableChannel(DMA_INSTANCE); + LL_DMA_DisableChannel(DMA_INSTANCE); } \ No newline at end of file diff --git a/applications/plugins/wav_player/wav_player_view.c b/applications/plugins/wav_player/wav_player_view.c index fdf08cb21..b2414d0b8 100644 --- a/applications/plugins/wav_player/wav_player_view.c +++ b/applications/plugins/wav_player/wav_player_view.c @@ -1,22 +1,5 @@ #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; } @@ -30,7 +13,7 @@ static void wav_player_view_draw_callback(Canvas* canvas, void* _model) { uint8_t y_pos = 0; // volume - x_pos = 124; + x_pos = 123; y_pos = 0; const float volume = (64 / 10.0f) * model->volume; canvas_draw_frame(canvas, x_pos, y_pos, 4, 64); diff --git a/applications/plugins/wav_player/wav_player_view.h b/applications/plugins/wav_player/wav_player_view.h index 246aeaf3e..72227c096 100644 --- a/applications/plugins/wav_player/wav_player_view.h +++ b/applications/plugins/wav_player/wav_player_view.h @@ -1,6 +1,16 @@ #pragma once #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #ifdef __cplusplus extern "C" { #endif @@ -18,6 +28,27 @@ typedef enum { typedef void (*WavPlayerCtrlCallback)(WavPlayerCtrl ctrl, void* context); +#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; + + + + + WavPlayerView* wav_player_view_alloc(); void wav_player_view_free(WavPlayerView* wav_view);