Update WAV Player, 16 bit support

by @LTVA1, in current condition has some issues with rewind on 16bit files, but plays them without problems
This commit is contained in:
MX 2023-03-19 02:20:23 +03:00
parent 56c11c70b3
commit 76d3f84a5e
No known key found for this signature in database
GPG Key ID: 7CCC66B7DBDD1C83
5 changed files with 136 additions and 9 deletions

View File

@ -1,4 +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.
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~~. Now supports 16-bit (ordinary) wav files too, both mono and stereo!
Original app by https://github.com/DrZlo13.
Original app by https://github.com/DrZlo13.

View File

@ -127,7 +127,7 @@ static void app_free(WavPlayerApp* app) {
// TODO: that works only with 8-bit 2ch audio
static bool fill_data(WavPlayerApp* app, size_t index) {
if(app->num_channels == 1) {
if(app->num_channels == 1 && app->bits_per_sample == 8) {
uint16_t* sample_buffer_start = &app->sample_buffer[index];
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count_half);
@ -166,7 +166,108 @@ static bool fill_data(WavPlayerApp* app, size_t index) {
return count != app->samples_count_half;
}
if(app->num_channels == 2) {
if(app->num_channels == 1 && app->bits_per_sample == 16) {
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) {
int16_t int_16 =
(((int16_t)app->tmp_buffer[i + 1] << 8) + (int16_t)app->tmp_buffer[i]);
float data = ((float)int_16 / 256.0 + 127.0);
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;
}
if(app->num_channels == 2 && app->bits_per_sample == 16) {
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 = 0; i < app->samples_count; i += 4) {
int16_t L = (((int16_t)app->tmp_buffer[i + 1] << 8) + (int16_t)app->tmp_buffer[i]);
int16_t R = (((int16_t)app->tmp_buffer[i + 3] << 8) + (int16_t)app->tmp_buffer[i + 2]);
int32_t int_16 = L / 2 + R / 2; // (L + R) / 2
float data = ((float)int_16 / 256.0 + 127.0);
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 / 4] = data;
}
count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
for(size_t i = 0; i < app->samples_count; i += 4) {
int16_t L = (((int16_t)app->tmp_buffer[i + 1] << 8) + (int16_t)app->tmp_buffer[i]);
int16_t R = (((int16_t)app->tmp_buffer[i + 3] << 8) + (int16_t)app->tmp_buffer[i + 2]);
int32_t int_16 = L / 2 + R / 2; // (L + R) / 2
float data = ((float)int_16 / 256.0 + 127.0);
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 / 4 + app->samples_count / 4] = data;
}
wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half);
return count != app->samples_count;
}
if(app->num_channels == 2 && app->bits_per_sample == 8) {
uint16_t* sample_buffer_start = &app->sample_buffer[index];
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
@ -270,6 +371,9 @@ static void app_run(WavPlayerApp* app) {
while(1) {
if(furi_message_queue_get(app->queue, &event, FuriWaitForever) == FuriStatusOk) {
if(event.type == WavPlayerEventHalfTransfer) {
wav_player_view_set_chans(app->view, app->num_channels);
wav_player_view_set_bits(app->view, app->bits_per_sample);
eof = fill_data(app, 0);
wav_player_view_set_current(app->view, stream_tell(app->stream));
if(eof) {
@ -280,6 +384,9 @@ static void app_run(WavPlayerApp* app) {
}
} else if(event.type == WavPlayerEventFullTransfer) {
wav_player_view_set_chans(app->view, app->num_channels);
wav_player_view_set_bits(app->view, app->bits_per_sample);
eof = fill_data(app, app->samples_count_half);
wav_player_view_set_current(app->view, stream_tell(app->stream));
if(eof) {

View File

@ -35,7 +35,7 @@ void wav_player_speaker_init(uint32_t sample_rate) {
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
TIM_InitStruct.Autoreload = 64000000 / sample_rate - 1; //to support various sample rates
LL_TIM_Init(SAMPLE_RATE_TIMER, &TIM_InitStruct);
@ -48,16 +48,12 @@ void wav_player_speaker_init(uint32_t sample_rate) {
//=========================================================
//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() {

View File

@ -12,6 +12,12 @@ static void wav_player_view_draw_callback(Canvas* canvas, void* _model) {
uint8_t x_pos = 0;
uint8_t y_pos = 0;
/*char buffer[20];
snprintf(buffer, sizeof(buffer), "%d", model->num_channels);
canvas_draw_str(canvas, 0, 10, buffer);
snprintf(buffer, sizeof(buffer), "%d", model->bits_per_sample);
canvas_draw_str(canvas, 0, 20, buffer);*/
// volume
x_pos = 123;
y_pos = 0;
@ -156,6 +162,18 @@ void wav_player_view_set_play(WavPlayerView* wav_view, bool play) {
wav_view->view, WavPlayerViewModel * model, { model->play = play; }, true);
}
void wav_player_view_set_chans(WavPlayerView* wav_view, uint16_t chn) {
furi_assert(wav_view);
with_view_model(
wav_view->view, WavPlayerViewModel * model, { model->num_channels = chn; }, true);
}
void wav_player_view_set_bits(WavPlayerView* wav_view, uint16_t bit) {
furi_assert(wav_view);
with_view_model(
wav_view->view, WavPlayerViewModel * model, { model->bits_per_sample = bit; }, true);
}
void wav_player_view_set_data(WavPlayerView* wav_view, uint16_t* data, size_t data_count) {
furi_assert(wav_view);
with_view_model(

View File

@ -43,6 +43,9 @@ typedef struct {
size_t end;
size_t current;
uint8_t data[DATA_COUNT];
uint16_t bits_per_sample;
uint16_t num_channels;
} WavPlayerViewModel;
WavPlayerView* wav_player_view_alloc();
@ -63,6 +66,9 @@ 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_bits(WavPlayerView* wav_view, uint16_t bit);
void wav_player_view_set_chans(WavPlayerView* wav_view, uint16_t chn);
void wav_player_view_set_ctrl_callback(WavPlayerView* wav_view, WavPlayerCtrlCallback callback);
void wav_player_view_set_context(WavPlayerView* wav_view, void* context);