Fixed and improved WAV Player by LTVA1

https://github.com/LTVA1/wav_player
This commit is contained in:
MX 2023-01-18 21:17:53 +03:00
parent 6aad6840f6
commit 0650178946
No known key found for this signature in database
GPG key ID: 6C4C311DFD4B4AB5
8 changed files with 229 additions and 102 deletions

View file

@ -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)

View file

@ -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.

View file

@ -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;

View file

@ -1,6 +1,18 @@
#pragma once
#include <toolbox/stream/stream.h>
#include <furi.h>
#include <furi_hal.h>
#include <cli/cli.h>
#include <gui/gui.h>
#include <stm32wbxx_ll_dma.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <gui/view_dispatcher.h>
#include <toolbox/stream/file_stream.h>
#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);

View file

@ -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,20 +121,23 @@ 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) {
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);
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count_half);
for(size_t i = count; i < app->samples_count; i++) {
for(size_t i = count; i < app->samples_count_half; i++) {
app->tmp_buffer[i] = 0;
}
for(size_t i = 0; i < app->samples_count; i += 2) {
//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
@ -174,6 +156,45 @@ static bool fill_data(WavPlayerApp* app, size_t index) {
data = 255;
}
//uint8_t data = app->tmp_buffer[i];
sample_buffer_start[i] = data;
}
wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half);
return count != 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);
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;
}
@ -182,6 +203,9 @@ static bool fill_data(WavPlayerApp* app, size_t index) {
return count != app->samples_count;
}
return true;
}
static void ctrl_callback(WavPlayerCtrl ctrl, void* ctx) {
FuriMessageQueue* event_queue = ctx;
WavPlayerEvent event;
@ -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);

View file

@ -2,14 +2,27 @@
#include <stm32wbxx_ll_tim.h>
#include <stm32wbxx_ll_dma.h>
#include <stm32wbxx_ll_gpio.h>
#include <furi_hal.h>
#include <furi_hal_gpio.h>
#include <furi_hal_resources.h>
//#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() {
void wav_player_speaker_init(uint32_t sample_rate)
{
LL_TIM_InitTypeDef TIM_InitStruct = {0};
TIM_InitStruct.Prescaler = 4;
TIM_InitStruct.Autoreload = 255;
//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};
@ -17,16 +30,46 @@ void wav_player_speaker_init() {
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(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(SAMPLE_RATE_TIMER);
LL_TIM_DisableCounter(SAMPLE_RATE_TIMER);
}
void wav_player_dma_init(uint32_t address, size_t size) {
@ -35,7 +78,7 @@ void wav_player_dma_init(uint32_t address, size_t 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_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);
@ -50,7 +93,7 @@ void wav_player_dma_init(uint32_t address, size_t size) {
void wav_player_dma_start() {
LL_DMA_EnableChannel(DMA_INSTANCE);
LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_SPEAKER_TIMER);
LL_TIM_EnableDMAReq_UPDATE(SAMPLE_RATE_TIMER);
}
void wav_player_dma_stop() {

View file

@ -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);

View file

@ -1,6 +1,16 @@
#pragma once
#include <gui/view.h>
#include <furi.h>
#include <furi_hal.h>
#include <cli/cli.h>
#include <gui/gui.h>
#include <stm32wbxx_ll_dma.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <gui/view_dispatcher.h>
#include <toolbox/stream/file_stream.h>
#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);