mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-18 10:51:54 +03:00
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:
parent
56c11c70b3
commit
76d3f84a5e
4
applications/external/wav_player/README.md
vendored
4
applications/external/wav_player/README.md
vendored
@ -1,4 +1,4 @@
|
|||||||
# WAV player
|
# 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.
|
||||||
|
111
applications/external/wav_player/wav_player.c
vendored
111
applications/external/wav_player/wav_player.c
vendored
@ -127,7 +127,7 @@ static void app_free(WavPlayerApp* app) {
|
|||||||
|
|
||||||
// TODO: that works only with 8-bit 2ch audio
|
// TODO: that works only with 8-bit 2ch audio
|
||||||
static bool fill_data(WavPlayerApp* app, size_t index) {
|
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];
|
uint16_t* sample_buffer_start = &app->sample_buffer[index];
|
||||||
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count_half);
|
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;
|
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];
|
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);
|
||||||
|
|
||||||
@ -270,6 +371,9 @@ static void app_run(WavPlayerApp* app) {
|
|||||||
while(1) {
|
while(1) {
|
||||||
if(furi_message_queue_get(app->queue, &event, FuriWaitForever) == FuriStatusOk) {
|
if(furi_message_queue_get(app->queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||||
if(event.type == WavPlayerEventHalfTransfer) {
|
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);
|
eof = fill_data(app, 0);
|
||||||
wav_player_view_set_current(app->view, stream_tell(app->stream));
|
wav_player_view_set_current(app->view, stream_tell(app->stream));
|
||||||
if(eof) {
|
if(eof) {
|
||||||
@ -280,6 +384,9 @@ static void app_run(WavPlayerApp* app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if(event.type == WavPlayerEventFullTransfer) {
|
} 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);
|
eof = fill_data(app, app->samples_count_half);
|
||||||
wav_player_view_set_current(app->view, stream_tell(app->stream));
|
wav_player_view_set_current(app->view, stream_tell(app->stream));
|
||||||
if(eof) {
|
if(eof) {
|
||||||
|
@ -35,7 +35,7 @@ void wav_player_speaker_init(uint32_t sample_rate) {
|
|||||||
TIM_InitStruct.Prescaler = 0;
|
TIM_InitStruct.Prescaler = 0;
|
||||||
//TIM_InitStruct.Autoreload = 1451; //64 000 000 / 1451 ~= 44100 Hz
|
//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);
|
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
|
//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(
|
furi_hal_gpio_init_ex(
|
||||||
&gpio_ext_pa6,
|
&gpio_ext_pa6,
|
||||||
GpioModeAltFunctionPushPull,
|
GpioModeAltFunctionPushPull,
|
||||||
GpioPullNo,
|
GpioPullNo,
|
||||||
GpioSpeedVeryHigh,
|
GpioSpeedVeryHigh,
|
||||||
GpioAltFn14TIM16);
|
GpioAltFn14TIM16);
|
||||||
//furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull);
|
|
||||||
//furi_hal_gpio_write(&gpio_ext_pa6, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wav_player_speaker_start() {
|
void wav_player_speaker_start() {
|
||||||
|
@ -12,6 +12,12 @@ static void wav_player_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
uint8_t x_pos = 0;
|
uint8_t x_pos = 0;
|
||||||
uint8_t y_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
|
// volume
|
||||||
x_pos = 123;
|
x_pos = 123;
|
||||||
y_pos = 0;
|
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);
|
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) {
|
void wav_player_view_set_data(WavPlayerView* wav_view, uint16_t* data, size_t data_count) {
|
||||||
furi_assert(wav_view);
|
furi_assert(wav_view);
|
||||||
with_view_model(
|
with_view_model(
|
||||||
|
@ -43,6 +43,9 @@ typedef struct {
|
|||||||
size_t end;
|
size_t end;
|
||||||
size_t current;
|
size_t current;
|
||||||
uint8_t data[DATA_COUNT];
|
uint8_t data[DATA_COUNT];
|
||||||
|
|
||||||
|
uint16_t bits_per_sample;
|
||||||
|
uint16_t num_channels;
|
||||||
} WavPlayerViewModel;
|
} WavPlayerViewModel;
|
||||||
|
|
||||||
WavPlayerView* wav_player_view_alloc();
|
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_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_ctrl_callback(WavPlayerView* wav_view, WavPlayerCtrlCallback callback);
|
||||||
|
|
||||||
void wav_player_view_set_context(WavPlayerView* wav_view, void* context);
|
void wav_player_view_set_context(WavPlayerView* wav_view, void* context);
|
||||||
|
Loading…
Reference in New Issue
Block a user