| | |
| | | #include "system.h" |
| | | #include "app_task.h" |
| | | |
| | | #include "audio_codec.h" |
| | | #include "board.h" |
| | | #include "application.h" |
| | | |
| | | #include <esp_log.h> |
| | | #include <freertos/FreeRTOS.h> |
| | |
| | | typedef struct { |
| | | char path[SDCARD_PATH_MAX_LEN]; |
| | | float speed; |
| | | int repeat; |
| | | bool power_off_after; |
| | | } mp3_player_task_arg_t; |
| | | |
| | | static TaskHandle_t mp3_player_task_handle; |
| | | static volatile bool mp3_stop_requested; |
| | | static volatile bool mp3_pause_requested; |
| | | static volatile int mp3_repeat_left; |
| | | |
| | | static std::vector<int16_t> convert_channels(const int16_t *input, size_t frame_count, uint8_t src_channels, uint8_t dst_channels) |
| | | { |
| | |
| | | char path[SDCARD_PATH_MAX_LEN]; |
| | | strlcpy(path, task_arg->path, sizeof(path)); |
| | | float speed = task_arg->speed; |
| | | int repeat = task_arg->repeat < 1 ? 1 : task_arg->repeat; |
| | | bool power_off_after = task_arg->power_off_after; |
| | | free(task_arg); |
| | | |
| | | FILE *fp = fopen(path, "rb"); |
| | |
| | | ESP_LOGE(TAG, "open MP3 failed: %s", path); |
| | | mp3_player_task_handle = nullptr; |
| | | mp3_stop_requested = false; |
| | | mp3_repeat_left = 0; |
| | | vTaskDelete(nullptr); |
| | | return; |
| | | } |
| | |
| | | fclose(fp); |
| | | mp3_player_task_handle = nullptr; |
| | | mp3_stop_requested = false; |
| | | mp3_repeat_left = 0; |
| | | vTaskDelete(nullptr); |
| | | return; |
| | | } |
| | |
| | | fclose(fp); |
| | | mp3_player_task_handle = nullptr; |
| | | mp3_stop_requested = false; |
| | | mp3_repeat_left = 0; |
| | | vTaskDelete(nullptr); |
| | | return; |
| | | } |
| | |
| | | ESP_LOGE(TAG, "no memory for MP3 playback"); |
| | | ret = ESP_AUDIO_ERR_MEM_LACK; |
| | | } else { |
| | | ESP_LOGI(TAG, "play MP3: %s, speed: %.2fx", path, speed); |
| | | ESP_LOGI(TAG, "play MP3: %s, speed: %.2fx, repeat: %d, power_off_after: %d", path, speed, repeat, power_off_after ? 1 : 0); |
| | | |
| | | if (wait_if_paused()) { |
| | | codec->EnableOutput(true); |
| | | } |
| | | |
| | | for (int pass = 0; pass < repeat && ret == ESP_AUDIO_ERR_OK && !mp3_stop_requested; ++pass) { |
| | | mp3_repeat_left = repeat - pass; |
| | | if (pass > 0) { |
| | | if (fseek(fp, 0, SEEK_SET) != 0) { |
| | | ESP_LOGE(TAG, "rewind MP3 failed: %s", path); |
| | | break; |
| | | } |
| | | esp_audio_simple_dec_close(decoder); |
| | | decoder = nullptr; |
| | | ret = esp_audio_simple_dec_open(&dec_cfg, &decoder); |
| | | if (ret != ESP_AUDIO_ERR_OK || !decoder) { |
| | | ESP_LOGE(TAG, "reopen MP3 decoder failed: %d", ret); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | while (ret == ESP_AUDIO_ERR_OK && !mp3_stop_requested) { |
| | | if (!wait_if_paused()) { |
| | |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (rate_cvt_handle) { |
| | |
| | | } |
| | | free(in_buf); |
| | | free(out_buf); |
| | | esp_audio_simple_dec_close(decoder); |
| | | if (decoder) { |
| | | esp_audio_simple_dec_close(decoder); |
| | | } |
| | | esp_audio_simple_dec_unregister_default(); |
| | | esp_audio_dec_unregister_default(); |
| | | fclose(fp); |
| | | |
| | | ESP_LOGI(TAG, "MP3 playback %s: %s", mp3_stop_requested ? "stopped" : "finished", path); |
| | | bool stopped = mp3_stop_requested; |
| | | bool should_power_off = power_off_after && !stopped; |
| | | mp3_player_task_handle = nullptr; |
| | | mp3_stop_requested = false; |
| | | mp3_pause_requested = false; |
| | | mp3_repeat_left = 0; |
| | | if (should_power_off) { |
| | | Application::GetInstance().RequestPowerOffStandby(); |
| | | } |
| | | Board_OnSkillPlaybackFinished(stopped, power_off_after); |
| | | vTaskDelete(nullptr); |
| | | } |
| | | |
| | | extern "C" esp_err_t SDCard_Play_MP3_File(const char *path) |
| | | { |
| | | return SDCard_Play_MP3_File_With_Speed(path, 1.0f); |
| | | return SDCard_Play_MP3_File_With_Speed_Repeat(path, 1.0f, 1, false); |
| | | } |
| | | |
| | | extern "C" esp_err_t SDCard_Play_MP3_File_With_Speed(const char *path, float speed) |
| | | { |
| | | return SDCard_Play_MP3_File_With_Speed_Repeat(path, speed, 1, false); |
| | | } |
| | | |
| | | extern "C" esp_err_t SDCard_Play_MP3_File_With_Speed_Repeat(const char *path, float speed, int repeat, bool power_off_after) |
| | | { |
| | | if (!path || path[0] == '\0' || !is_supported_speed(speed)) { |
| | | return ESP_ERR_INVALID_ARG; |
| | | } |
| | | if (repeat < 1) { |
| | | repeat = 1; |
| | | } |
| | | |
| | | bool start_paused = mp3_pause_requested; |
| | |
| | | } |
| | | strlcpy(arg->path, path, sizeof(arg->path)); |
| | | arg->speed = speed; |
| | | arg->repeat = repeat; |
| | | arg->power_off_after = power_off_after; |
| | | mp3_stop_requested = false; |
| | | mp3_pause_requested = start_paused; |
| | | mp3_repeat_left = repeat; |
| | | |
| | | BaseType_t ret = xTaskCreate(mp3_player_task, "mp3_player", MP3_PLAYER_STACK_SIZE, arg, MP3_PLAYER_PRIORITY, &mp3_player_task_handle); |
| | | if (ret != pdPASS) { |
| | | free(arg); |
| | | mp3_player_task_handle = nullptr; |
| | | mp3_repeat_left = 0; |
| | | return ESP_FAIL; |
| | | } |
| | | |
| | |
| | | { |
| | | return mp3_player_task_handle != nullptr && mp3_pause_requested; |
| | | } |
| | | |
| | | extern "C" int SDCard_Get_Repeat_Left(void) |
| | | { |
| | | return mp3_repeat_left; |
| | | } |