201 lines
6.8 KiB
C

/*
* ESPRESSIF MIT License
*
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "audio_element.h"
#include "audio_error.h"
#include "audio_mem.h"
#include "tone_partition.h"
#include "tone_stream.h"
#include <inttypes.h>
static const char *TAG = "TONE_STREAM";
/**
* @brief Parameters of each tone
*/
typedef struct tone_stream {
audio_stream_type_t type; /*!< File operation type */
bool is_open; /*!< Tone stream status */
bool use_delegate; /*!< Tone read with delegate*/
tone_partition_handle_t tone_handle; /*!< Tone partition's operation handle*/
tone_file_info_t cur_file; /*!< Address to read tone file */
const char *partition_label; /*!< Label of tone stored in flash */
} tone_stream_t;
static esp_err_t _tone_open(audio_element_handle_t self)
{
tone_stream_t *stream = (tone_stream_t *)audio_element_getdata(self);
if (stream->is_open) {
ESP_LOGE(TAG, "already opened");
return ESP_FAIL;
}
stream->tone_handle = tone_partition_init(stream->partition_label, stream->use_delegate);
if (stream->tone_handle == NULL) {
return ESP_FAIL;
}
char *flash_url = audio_element_get_uri(self);
flash_url += strlen("flash://tone/");
char *temp = strchr(flash_url, '_');
char find_num[3] = { 0 };
int file_index = 0;
if (temp != NULL) {
strncpy(find_num, flash_url, temp - flash_url);
file_index = strtoul(find_num, 0, 10);
ESP_LOGD(TAG, "Wanted read flash tone index is %d", file_index);
} else {
ESP_LOGE(TAG, "Tone file name is not correct!");
return ESP_FAIL;
}
tone_partition_get_file_info(stream->tone_handle, file_index, &stream->cur_file);
ESP_LOGI(TAG, "Tone offset:%08"PRIX32", Tone length:%"PRIu32", pos:%d\n", stream->cur_file.song_adr, stream->cur_file.song_len, file_index);
if (stream->cur_file.song_len <= 0) {
ESP_LOGE(TAG, "Mayebe the flash tone is empty, please ensure the flash's contex");
return ESP_FAIL;
}
audio_element_info_t info = { 0 };
info.total_bytes = stream->cur_file.song_len;
audio_element_setdata(self, stream);
audio_element_set_total_bytes(self, info.total_bytes);
stream->is_open = true;
return ESP_OK;
}
static int _tone_read(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context)
{
audio_element_info_t info = { 0 };
tone_stream_t *stream = NULL;
stream = (tone_stream_t *)audio_element_getdata(self);
audio_element_getinfo(self, &info);
if (info.byte_pos + len > info.total_bytes) {
len = info.total_bytes - info.byte_pos;
}
if (ESP_OK != tone_partition_file_read(stream->tone_handle, &stream->cur_file, info.byte_pos, buffer, len)) {
ESP_LOGE(TAG, "get tone data error, line:%d", __LINE__);
return ESP_FAIL;
}
if (len <= 0) {
ESP_LOGW(TAG, "No more data,ret:%d ,info.byte_pos:%llu", len, info.byte_pos);
return ESP_OK;
}
audio_element_update_byte_pos(self, len);
return len;
}
static int _tone_process(audio_element_handle_t self, char *in_buffer, int in_len)
{
int r_size = audio_element_input(self, in_buffer, in_len);
int w_size = 0;
if (r_size > 0) {
w_size = audio_element_output(self, in_buffer, r_size);
} else {
w_size = r_size;
}
return w_size;
}
static esp_err_t _tone_close(audio_element_handle_t self)
{
tone_stream_t *stream = (tone_stream_t *)audio_element_getdata(self);
if (stream->is_open) {
stream->is_open = false;
}
tone_partition_deinit(stream->tone_handle);
stream->tone_handle = NULL;
if (AEL_STATE_PAUSED != audio_element_get_state(self)) {
audio_element_set_byte_pos(self, 0);
}
return ESP_OK;
}
static esp_err_t _tone_destroy(audio_element_handle_t self)
{
tone_stream_t *stream = (tone_stream_t *)audio_element_getdata(self);
audio_free(stream);
return ESP_OK;
}
audio_element_handle_t tone_stream_init(tone_stream_cfg_t *config)
{
audio_element_handle_t el;
tone_stream_t *stream = audio_calloc(1, sizeof(tone_stream_t));
AUDIO_MEM_CHECK(TAG, stream, return NULL);
audio_element_cfg_t cfg = DEFAULT_AUDIO_ELEMENT_CONFIG();
cfg.open = _tone_open;
cfg.close = _tone_close;
cfg.process = _tone_process;
cfg.destroy = _tone_destroy;
cfg.task_stack = config->task_stack;
cfg.task_prio = config->task_prio;
cfg.task_core = config->task_core;
cfg.out_rb_size = config->out_rb_size;
cfg.buffer_len = config->buf_sz;
cfg.stack_in_ext = config->extern_stack;
if (cfg.stack_in_ext == true && config->use_delegate == false) {
ESP_LOGE(TAG, "Tone stream must read flash with delegate when stack is allocate in external ram");
goto _tone_init_exit;
}
if (cfg.buffer_len == 0) {
cfg.buffer_len = TONE_STREAM_BUF_SIZE;
}
cfg.tag = "flash";
stream->type = config->type;
stream->use_delegate = config->use_delegate;
if (config->label == NULL) {
ESP_LOGE(TAG, "Please set your tone label");
return NULL;
} else {
stream->partition_label = config->label;
}
if (config->type == AUDIO_STREAM_READER) {
cfg.read = _tone_read;
} else if (config->type == AUDIO_STREAM_WRITER) {
ESP_LOGE(TAG, "No writer for tone stream");
goto _tone_init_exit;
}
el = audio_element_init(&cfg);
AUDIO_MEM_CHECK(TAG, el, goto _tone_init_exit);
audio_element_setdata(el, stream);
return el;
_tone_init_exit:
audio_free(stream);
return NULL;
}