diff --git a/applications/external/wav_player/README.md b/applications/external/wav_player/README.md index e5ea73c2f9..89052646e5 100644 --- a/applications/external/wav_player/README.md +++ b/applications/external/wav_player/README.md @@ -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. \ No newline at end of file +Original app by https://github.com/DrZlo13. diff --git a/applications/external/wav_player/wav_player.c b/applications/external/wav_player/wav_player.c index 3fb8b1ea5e..ce14286828 100644 --- a/applications/external/wav_player/wav_player.c +++ b/applications/external/wav_player/wav_player.c @@ -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) { diff --git a/applications/external/wav_player/wav_player_hal.c b/applications/external/wav_player/wav_player_hal.c index ba049321d3..40f7a3ff55 100644 --- a/applications/external/wav_player/wav_player_hal.c +++ b/applications/external/wav_player/wav_player_hal.c @@ -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() { diff --git a/applications/external/wav_player/wav_player_view.c b/applications/external/wav_player/wav_player_view.c index b2414d0b88..285b3dd331 100644 --- a/applications/external/wav_player/wav_player_view.c +++ b/applications/external/wav_player/wav_player_view.c @@ -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( diff --git a/applications/external/wav_player/wav_player_view.h b/applications/external/wav_player/wav_player_view.h index 3ef9251fe5..b7b0ef908d 100644 --- a/applications/external/wav_player/wav_player_view.h +++ b/applications/external/wav_player/wav_player_view.h @@ -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);