demo工程暂存 优化菜单界面UI和功能
This commit is contained in:
72
MCU/components/modules/audio/algorithm/algorithm.c
Normal file
72
MCU/components/modules/audio/algorithm/algorithm.c
Normal file
@ -0,0 +1,72 @@
|
||||
#include "audio_rpmsg.h"
|
||||
#include "algorithm.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "dsp.h"
|
||||
|
||||
void *algorithm_init(uint8_t algo_sel, uint32_t sample_rate, uint8_t ns_level, uint16_t agc_mode, uint32_t *frame_size)
|
||||
{
|
||||
struct rpmsg_sync_msg_algorithm_init *sync_msg;
|
||||
void *result;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_algorithm_init));
|
||||
if (sync_msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
sync_msg->algo_sel = algo_sel;
|
||||
sync_msg->sample_rate = sample_rate;
|
||||
sync_msg->ns_level = ns_level;
|
||||
sync_msg->agc_mode = agc_mode;
|
||||
sync_msg->frame_size = frame_size;
|
||||
|
||||
rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_AUDIO_ALGO_INIT, sync_msg, (uint32_t *)&result);
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int algorithm_launch(void *handle, int16_t *farend, int16_t *nearend, int16_t **out)
|
||||
{
|
||||
struct rpmsg_sync_msg_algorithm_launch *sync_msg;
|
||||
int result;
|
||||
bool trans_addr = false;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_algorithm_launch));
|
||||
if (sync_msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (((uint32_t)farend >= DSP_DRAM_MCU_BASE_ADDR) && ((uint32_t)farend < (DSP_DRAM_MCU_BASE_ADDR+DSP_DRAM_SIZE))) {
|
||||
farend = (void *)MCU_SRAM_2_DSP_DRAM(farend);
|
||||
}
|
||||
if (((uint32_t)nearend >= DSP_DRAM_MCU_BASE_ADDR) && ((uint32_t)nearend < (DSP_DRAM_MCU_BASE_ADDR+DSP_DRAM_SIZE))) {
|
||||
nearend = (void *)MCU_SRAM_2_DSP_DRAM(nearend);
|
||||
}
|
||||
|
||||
sync_msg->handle = handle;
|
||||
sync_msg->farend = farend;
|
||||
sync_msg->nearend = nearend;
|
||||
sync_msg->out = out;
|
||||
|
||||
if (*out == NULL) {
|
||||
trans_addr = true;
|
||||
}
|
||||
|
||||
rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_AUDIO_ALGO_LAUNCH, sync_msg, NULL);
|
||||
|
||||
if (trans_addr) {
|
||||
*out = (void *)DSP_DRAM_2_MCU_SRAM(*out);
|
||||
}
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void algorithm_destroy(void *handle)
|
||||
{
|
||||
struct rpmsg_sync_msg_algorithm_destroy_t sync_msg;
|
||||
|
||||
sync_msg.handle = handle;
|
||||
rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_AUDIO_ALGO_RELEASE, (void *)&sync_msg, NULL);
|
||||
}
|
16
MCU/components/modules/audio/algorithm/algorithm.h
Normal file
16
MCU/components/modules/audio/algorithm/algorithm.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef _ALGORITHM_H
|
||||
#define _ALGORITHM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum audio_algo_sel {
|
||||
AUDIO_ALGO_SEL_AEC = 0x01,
|
||||
AUDIO_ALGO_SEL_NS = 0x02,
|
||||
AUDIO_ALGO_SEL_AGC = 0x04,
|
||||
};
|
||||
|
||||
void *algorithm_init(uint8_t algo_sel, uint32_t sample_rate, uint8_t ns_level, uint16_t agc_mode, uint32_t *frame_size);
|
||||
int algorithm_launch(void *handle, int16_t *farend, int16_t *nearend, int16_t **out);
|
||||
void algorithm_destroy(void *handle);
|
||||
|
||||
#endif // _ALGORITHM_H
|
44
MCU/components/modules/audio/audio_common.h
Normal file
44
MCU/components/modules/audio/audio_common.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef _AUDIO_COMMON_H
|
||||
#define _AUDIO_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "co_list.h"
|
||||
|
||||
#define AUDIO_SPECIAL_LENGTH_FOR_PLC 0xFFFFFFFF
|
||||
|
||||
typedef enum {
|
||||
AUDIO_RET_OUTPUT_ALMOTE_FULL = 4,
|
||||
AUDIO_RET_INPUT_ALMOTE_FULL = 3,
|
||||
AUDIO_RET_NEED_MORE = 2,
|
||||
AUDIO_RET_PENDING = 1,
|
||||
AUDIO_RET_OK = 0,
|
||||
AUDIO_RET_FAILED = -1,
|
||||
AUDIO_RET_ERR_CREATED = -2,
|
||||
AUDIO_RET_UNACCEPTABLE_SAMPLE_RATE = -3,
|
||||
AUDIO_RET_NOT_ALLOWED = -4,
|
||||
} audio_ret_t;
|
||||
|
||||
typedef enum {
|
||||
AUDIO_TYPE_PCM,
|
||||
AUDIO_TYPE_SBC,
|
||||
AUDIO_TYPE_MP3,
|
||||
AUDIO_TYPE_AAC,
|
||||
AUDIO_TYPE_CVSD,
|
||||
AUDIO_TYPE_MSBC,
|
||||
AUDIO_TYPE_LC3,
|
||||
} audio_type_t;
|
||||
|
||||
typedef enum {
|
||||
AUDIO_CHANNELS_MONO = 1,
|
||||
AUDIO_CHANNELS_STEREO = 2,
|
||||
} audio_channels_t;
|
||||
|
||||
typedef struct {
|
||||
struct co_list_hdr hdr;
|
||||
bool valid;
|
||||
uint32_t length;
|
||||
uint32_t offset;
|
||||
uint8_t buffer[];
|
||||
} audio_data_element_t;
|
||||
|
||||
#endif // _AUDIO_COMMON_H
|
1054
MCU/components/modules/audio/audio_decoder.c
Normal file
1054
MCU/components/modules/audio/audio_decoder.c
Normal file
File diff suppressed because it is too large
Load Diff
190
MCU/components/modules/audio/audio_decoder.h
Normal file
190
MCU/components/modules/audio/audio_decoder.h
Normal file
@ -0,0 +1,190 @@
|
||||
#ifndef _AUDIO_DECODER_H
|
||||
#define _AUDIO_DECODER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "co_list.h"
|
||||
#include "codec.h"
|
||||
#include "resample.h"
|
||||
|
||||
#include "audio_common.h"
|
||||
|
||||
typedef struct {
|
||||
struct co_list_hdr hdr;
|
||||
|
||||
/* sample count in buffer */
|
||||
uint16_t samples;
|
||||
uint16_t offset;
|
||||
|
||||
int16_t pcm[];
|
||||
} audio_decoder_pcm_data_t;
|
||||
|
||||
typedef struct {
|
||||
struct co_list_hdr hdr;
|
||||
|
||||
/* ====Internal Usage==== */
|
||||
/*
|
||||
* true: fill zero when no enough data is available.
|
||||
* false: only return available only.
|
||||
*/
|
||||
uint8_t immediate;
|
||||
uint32_t rd_ptr;
|
||||
uint32_t missed_samples;
|
||||
} audio_decoder_output_t;
|
||||
|
||||
typedef struct _audio_decoder_t {
|
||||
struct co_list_hdr hdr;
|
||||
|
||||
/* reserved for upper layer */
|
||||
void (*req_dec_cb)(struct _audio_decoder_t *);
|
||||
|
||||
uint32_t current_sample_rate;
|
||||
|
||||
struct codec_decoder_handle *decoder;
|
||||
void *resample;
|
||||
|
||||
/* ====Internal Usage==== */
|
||||
/* Store decoded PCM dat */
|
||||
struct co_list pcm_list;
|
||||
/* current write pointer in Mixed pcm buffer, unit is sample */
|
||||
uint32_t wr_ptr;
|
||||
/* current working state */
|
||||
uint8_t state;
|
||||
} audio_decoder_t;
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_add
|
||||
*
|
||||
* @brief Add a new decoder to created audio decoder module. More than one decoder can be
|
||||
* added into audio decoder module, the decoded PCM data from different decoders
|
||||
* will be mixed into one PCM stream.
|
||||
*
|
||||
* @param type: audio type, @ref audio_type_t.
|
||||
* req_dec_cb: When available PCM data is less than a certain threshold, this
|
||||
* function will be call to request a new decode operation.
|
||||
*
|
||||
* @return created decoder handler, NULL will be returned when executation is failed.
|
||||
*/
|
||||
audio_decoder_t *audio_decoder_add(audio_type_t type, void (*req_dec_cb)(audio_decoder_t *));
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_remove
|
||||
*
|
||||
* @brief Remove a created audio decoder.
|
||||
*
|
||||
* @param decoder: decoder handler to be removed.
|
||||
*/
|
||||
void audio_decoder_remove(audio_decoder_t *decoder);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_start
|
||||
*
|
||||
* @brief After a decoder is added, call this function to start the decoder.
|
||||
*
|
||||
* @param decoder: decoder handler to start.
|
||||
*/
|
||||
void audio_decoder_start(audio_decoder_t *decoder);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_stop
|
||||
*
|
||||
* @brief After a decoder is started, call this function to stop the decoder.
|
||||
*
|
||||
* @param decoder: decoder handler to stop.
|
||||
*/
|
||||
void audio_decoder_stop(audio_decoder_t *decoder);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_output_add
|
||||
*
|
||||
* @brief Add an output to created audio decoder module. More than one output can be
|
||||
* added into audio decoder module. The requester should call audio_decoder_get_pcm
|
||||
* to read mixed PCM stream periodically. If mixed PCM stream is not fetched
|
||||
* on time by any output, the decoder module will be blocked.
|
||||
*
|
||||
* @param immediate: this parameter indicates the operation when no enough data is
|
||||
* available in mixed PCM stream. True: fill left space with zero, False:
|
||||
* just fill buffer with available PCM data.
|
||||
* channels: fill output buffer with mono or stereo data.
|
||||
*
|
||||
* @return created decoder output handler, NULL will be returned when executation is failed.
|
||||
*/
|
||||
audio_decoder_output_t *audio_decoder_output_add(uint8_t immediate, uint8_t channels);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_output_add
|
||||
*
|
||||
* @brief Add an output to created audio decoder module. More than one output can be
|
||||
* added into audio decoder module. The requester should call audio_decoder_get_pcm
|
||||
* to read mixed PCM stream periodically.
|
||||
*
|
||||
* @param type: audio type, @ref audio_type_t.
|
||||
* req_dec_cb: When available PCM data is less than a certain threshold, this
|
||||
* function will be call to request a new decode operation.
|
||||
*
|
||||
* @return created decoder handler, NULL will be returned when executation is failed.
|
||||
*/
|
||||
void audio_decoder_output_remove(audio_decoder_output_t *output);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_decode
|
||||
*
|
||||
* @brief Called by upper layer to start a new decode operation. PCM list of this decoder
|
||||
* will be checked at the beginning of this function, mixed PCM stream will be
|
||||
* filled with these data. If mixed PCM stream is full enough, this function will
|
||||
* return without decoding incoming raw data. This strategy can avoid PCM list becoming
|
||||
* too long.
|
||||
*
|
||||
* @param decoder: decoder handler.
|
||||
* buffer: origin raw data buffer.
|
||||
* length: how many available data in buffer, this field will be updated with
|
||||
* used data length before return from this function.
|
||||
*
|
||||
* @return PCM buffer level status of this decoder, @ref audio_ret_t.
|
||||
*/
|
||||
int audio_decoder_decode(audio_decoder_t *decoder, const uint8_t *buffer, uint32_t* length);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_get_pcm
|
||||
*
|
||||
* @brief used by output to fetch PCM data.
|
||||
*
|
||||
* @param output: output handler.
|
||||
* pcm: buffer to store PCM data.
|
||||
* samples: number of request samples.
|
||||
* channels: mono(1) or stereo(2), @ref audio_channels_t.
|
||||
*
|
||||
* @return actual saved samples into buffer.
|
||||
*/
|
||||
uint32_t audio_decoder_get_pcm(audio_decoder_output_t *output, int16_t *pcm, uint32_t samples, uint8_t channels);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_init
|
||||
*
|
||||
* @brief Init audio decoder module.
|
||||
*
|
||||
* @param channels: channel numbers stored in internal mixed PCM buffer.
|
||||
* out_sample_rate: PCM sample rate stored in internal mixed PCM buffer.
|
||||
*
|
||||
* @return init result, @ref audio_ret_t.
|
||||
*/
|
||||
int audio_decoder_init(uint8_t channels, uint32_t out_sample_rate);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decoder_destroy
|
||||
*
|
||||
* @brief Deinit audio decoder module.
|
||||
*/
|
||||
void audio_decoder_destroy(void);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_decorder_is_started
|
||||
*
|
||||
* @brief check audio decorder is started or not.
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
bool audio_decoder_is_started(void);
|
||||
|
||||
#endif //_AUDIO_DECODER_H
|
||||
|
200
MCU/components/modules/audio/audio_encoder.c
Normal file
200
MCU/components/modules/audio/audio_encoder.c
Normal file
@ -0,0 +1,200 @@
|
||||
#include "audio_encoder.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
audio_encoder_t *audio_encoder_init(audio_type_t type, uint8_t channels, uint32_t sample_rate, uint16_t frame_max_length, void *param)
|
||||
{
|
||||
audio_encoder_t *encoder;
|
||||
|
||||
encoder = pvPortMalloc(sizeof(audio_encoder_t));
|
||||
encoder->type = type;
|
||||
encoder->channels = channels;
|
||||
encoder->sample_rate = sample_rate;
|
||||
|
||||
switch (type) {
|
||||
case AUDIO_TYPE_SBC:
|
||||
encoder->encoder = codec_encoder_init(CODEC_ENCODER_TYPE_SBC, param);
|
||||
break;
|
||||
case AUDIO_TYPE_MSBC:
|
||||
encoder->encoder = codec_encoder_init(CODEC_ENCODER_TYPE_MSBC, param);
|
||||
break;
|
||||
case AUDIO_TYPE_AAC:
|
||||
encoder->encoder = codec_encoder_init(CODEC_ENCODER_TYPE_AAC, param);
|
||||
break;
|
||||
default:
|
||||
goto __err;
|
||||
}
|
||||
encoder->resampler = NULL;
|
||||
|
||||
encoder->frame_count = 0;
|
||||
encoder->frame_max_length = frame_max_length;
|
||||
co_list_init(&encoder->frame_list);
|
||||
encoder->frame_tmp = NULL;
|
||||
|
||||
return encoder;
|
||||
|
||||
__err:
|
||||
vPortFree(encoder);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void audio_encoder_destroy(audio_encoder_t *encoder)
|
||||
{
|
||||
audio_encoder_frame_t *frame;
|
||||
|
||||
if (encoder->frame_tmp) {
|
||||
vPortFree(encoder->frame_tmp);
|
||||
}
|
||||
|
||||
do {
|
||||
frame = (void *)co_list_pop_front(&encoder->frame_list);
|
||||
if (frame) {
|
||||
vPortFree(frame);
|
||||
}
|
||||
} while (frame);
|
||||
|
||||
codec_encoder_destroy(encoder->encoder);
|
||||
if (encoder->resampler) {
|
||||
resample_destroy(encoder->resampler);
|
||||
}
|
||||
|
||||
vPortFree(encoder);
|
||||
}
|
||||
|
||||
static void save_encoded_data(audio_encoder_t *encoder, const uint8_t *buffer, uint32_t length)
|
||||
{
|
||||
if (encoder->frame_tmp) {
|
||||
uint32_t last_space = encoder->frame_max_length - encoder->frame_tmp->length;
|
||||
|
||||
if (length <= last_space) {
|
||||
memcpy(&encoder->frame_tmp->data[encoder->frame_tmp->length], buffer, length);
|
||||
encoder->frame_tmp->length += length;
|
||||
}
|
||||
else {
|
||||
GLOBAL_INT_DISABLE();
|
||||
co_list_push_back(&encoder->frame_list, &encoder->frame_tmp->hdr);
|
||||
encoder->frame_count++;
|
||||
encoder->frame_tmp = NULL;
|
||||
GLOBAL_INT_RESTORE();
|
||||
save_encoded_data(encoder, buffer, length);
|
||||
}
|
||||
}
|
||||
else {
|
||||
audio_encoder_frame_t *frame;
|
||||
|
||||
frame = pvPortMalloc(sizeof(audio_encoder_frame_t) + encoder->frame_max_length);
|
||||
if (frame) {
|
||||
memcpy(&frame->data[0], buffer, length);
|
||||
frame->length = length;
|
||||
GLOBAL_INT_DISABLE();
|
||||
encoder->frame_tmp = frame;
|
||||
GLOBAL_INT_RESTORE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int audio_encoder_encode(audio_encoder_t *encoder, const uint8_t *buffer, uint32_t length, uint8_t channels, uint32_t sample_rate)
|
||||
{
|
||||
if ((encoder == NULL)
|
||||
|| (encoder->channels != channels)) {
|
||||
return AUDIO_RET_FAILED;
|
||||
}
|
||||
|
||||
/* check whether resample is needed. */
|
||||
if (sample_rate == encoder->sample_rate) {
|
||||
if (encoder->resampler) {
|
||||
resample_destroy(encoder->resampler);
|
||||
encoder->resampler = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (encoder->resampler) {
|
||||
resample_destroy(encoder->resampler);
|
||||
}
|
||||
else {
|
||||
enum resample_type type = resample_get_type(sample_rate, encoder->sample_rate);
|
||||
if (type != RESAMPLE_TYPE_INVALID) {
|
||||
return AUDIO_RET_UNACCEPTABLE_SAMPLE_RATE;
|
||||
}
|
||||
encoder->resampler = resample_init(type, channels);
|
||||
}
|
||||
}
|
||||
|
||||
while (length) {
|
||||
if (encoder->resampler) {
|
||||
uint32_t input_length = length;
|
||||
uint32_t output_length;
|
||||
uint8_t *out_buffer = NULL;
|
||||
resample_exec(encoder->resampler, buffer, &input_length, &out_buffer, &output_length);
|
||||
while (output_length) {
|
||||
uint32_t encode_input_length = output_length;
|
||||
uint32_t encode_output_length;
|
||||
uint8_t *encode_out_buffer = NULL;
|
||||
codec_encoder_encode(encoder->encoder, out_buffer, &encode_input_length, &encode_out_buffer, &encode_output_length);
|
||||
if (encode_output_length) {
|
||||
save_encoded_data(encoder, encode_out_buffer, encode_output_length);
|
||||
}
|
||||
out_buffer += encode_input_length;
|
||||
output_length -= encode_input_length;
|
||||
}
|
||||
buffer += input_length;
|
||||
length -= input_length;
|
||||
}
|
||||
else {
|
||||
uint32_t encode_input_length = length;
|
||||
uint32_t encode_output_length = 0;
|
||||
uint8_t *encode_out_buffer = NULL;
|
||||
codec_encoder_encode(encoder->encoder, buffer, &encode_input_length, &encode_out_buffer, &encode_output_length);
|
||||
if (encode_output_length) {
|
||||
// uint8_t *ptr = encode_out_buffer;
|
||||
// for(uint32_t i=0;i<encode_output_length;)
|
||||
// {
|
||||
// printf("%02x", ptr[i]);
|
||||
// i++;
|
||||
// if((i%128) == 0)
|
||||
// {
|
||||
// printf("\n");
|
||||
// }
|
||||
// }
|
||||
save_encoded_data(encoder, encode_out_buffer, encode_output_length);
|
||||
}
|
||||
buffer += encode_input_length;
|
||||
length -= encode_input_length;
|
||||
}
|
||||
}
|
||||
|
||||
return AUDIO_RET_OK;
|
||||
}
|
||||
|
||||
int audio_encoder_get_frame_count(audio_encoder_t *encoder)
|
||||
{
|
||||
if (encoder) {
|
||||
return encoder->frame_count;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
audio_encoder_frame_t *audio_encoder_frame_pop(audio_encoder_t *encoder)
|
||||
{
|
||||
if (encoder) {
|
||||
audio_encoder_frame_t *frame;
|
||||
GLOBAL_INT_DISABLE();
|
||||
frame = (void *)co_list_pop_front(&encoder->frame_list);
|
||||
if (frame) {
|
||||
encoder->frame_count--;
|
||||
}
|
||||
GLOBAL_INT_RESTORE();
|
||||
return frame;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void audio_encoder_frame_release(audio_encoder_frame_t *frame)
|
||||
{
|
||||
if (frame) {
|
||||
vPortFree(frame);
|
||||
}
|
||||
}
|
103
MCU/components/modules/audio/audio_encoder.h
Normal file
103
MCU/components/modules/audio/audio_encoder.h
Normal file
@ -0,0 +1,103 @@
|
||||
#ifndef _AUDIO_ENCODER_H
|
||||
#define _AUDIO_ENCODER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "co_list.h"
|
||||
#include "codec.h"
|
||||
#include "resample.h"
|
||||
|
||||
#include "audio_common.h"
|
||||
|
||||
typedef struct {
|
||||
struct co_list_hdr hdr;
|
||||
|
||||
uint16_t length;
|
||||
uint8_t data[];
|
||||
} audio_encoder_frame_t;
|
||||
|
||||
typedef struct {
|
||||
audio_type_t type;
|
||||
uint8_t channels;
|
||||
uint32_t sample_rate;
|
||||
|
||||
void *encoder;
|
||||
void *resampler;
|
||||
|
||||
uint8_t frame_count;
|
||||
uint16_t frame_max_length;
|
||||
struct co_list frame_list;
|
||||
audio_encoder_frame_t *frame_tmp;
|
||||
} audio_encoder_t;
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_encoder_init
|
||||
*
|
||||
* @brief Init audio encoder module.
|
||||
*
|
||||
* @param type: audio encoder type.
|
||||
* channels: mono or stereo.
|
||||
* sample_rate: input PCM sample rate.
|
||||
* frame_max_length:
|
||||
* param: decoder parameter.
|
||||
*
|
||||
* @return audio encoder handler, NULL will be returned when executation is failed.
|
||||
*/
|
||||
audio_encoder_t *audio_encoder_init(audio_type_t type, uint8_t channels, uint32_t sample_rate, uint16_t frame_max_length, void *param);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_encoder_destroy
|
||||
*
|
||||
* @brief destroy created audio encoder module.
|
||||
*
|
||||
* @param encoder: encoder handler.
|
||||
*/
|
||||
void audio_encoder_destroy(audio_encoder_t *encoder);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_encoder_encode
|
||||
*
|
||||
* @brief encode input PCM data.
|
||||
*
|
||||
* @param encoder: encoder handler.
|
||||
* buffer: PCM buffer.
|
||||
* length: size of input PCM data, unit is bytes.
|
||||
* channels: mono or stereo of input PCM data
|
||||
* sample_rate: sample rate of input PCM data.
|
||||
*
|
||||
* @return encoder result, @ref audio_ret_t.
|
||||
*/
|
||||
int audio_encoder_encode(audio_encoder_t *encoder, const uint8_t *buffer, uint32_t length, uint8_t channels, uint32_t sample_rate);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_encoder_get_frame_count
|
||||
*
|
||||
* @brief Get the number of frames are stored in encoder module.
|
||||
*
|
||||
* @param encoder: encoder handler.
|
||||
*
|
||||
* @return the number of frames are stored in encoder module.
|
||||
*/
|
||||
int audio_encoder_get_frame_count(audio_encoder_t *encoder);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_encoder_frame_pop
|
||||
*
|
||||
* @brief Get the head of frames stored in encoder module.
|
||||
*
|
||||
* @param encoder: encoder handler.
|
||||
*
|
||||
* @return the head of frames stored in encoder module.
|
||||
*/
|
||||
audio_encoder_frame_t *audio_encoder_frame_pop(audio_encoder_t *encoder);
|
||||
|
||||
/************************************************************************************
|
||||
* @fn audio_encoder_frame_release
|
||||
*
|
||||
* @brief Release encoded frame.
|
||||
*
|
||||
* @param frame: frame to be released.
|
||||
*/
|
||||
void audio_encoder_frame_release(audio_encoder_frame_t *frame);
|
||||
|
||||
#endif //_AUDIO_ENCODER_H
|
779
MCU/components/modules/audio/audio_hw.c
Normal file
779
MCU/components/modules/audio/audio_hw.c
Normal file
@ -0,0 +1,779 @@
|
||||
#include "audio_hw.h"
|
||||
#include "audio_common.h"
|
||||
|
||||
#include "fr30xx.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
#define AUDIO_HW_PDM_RX_INT_LEVEL 16
|
||||
#define AUDIO_HW_STORE_FRAME_COUNT 40
|
||||
|
||||
struct audio_hw_env_t {
|
||||
struct co_list hw_list;
|
||||
};
|
||||
|
||||
struct audio_hw_handle_t {
|
||||
audio_hw_t *audio_hw;
|
||||
void *hw_handle;
|
||||
};
|
||||
|
||||
static struct audio_hw_env_t audio_hw_env = {0};
|
||||
static struct audio_hw_handle_t i2s_hw_table[3];
|
||||
static struct audio_hw_handle_t pdm_hw_table[3];
|
||||
|
||||
static struct audio_hw_handle_t codec_hw;
|
||||
|
||||
static void i2s_rx_callback(I2S_HandleTypeDef *i2s_handle)
|
||||
{
|
||||
uint8_t index;
|
||||
audio_hw_t *hw;
|
||||
|
||||
for (index=0; index<3; index++) {
|
||||
if (i2s_hw_table[index].hw_handle == i2s_handle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index < 3) {
|
||||
hw = i2s_hw_table[index].audio_hw;
|
||||
|
||||
/* store data into internal buffer */
|
||||
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||||
int16_t *pcm = (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr];
|
||||
for (uint32_t i=0; i<I2S_FIFO_HALF_DEPTH;) {
|
||||
pcm[i++] = i2s_handle->I2Sx->DATA_L;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint32_t *pcm = (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr];
|
||||
for (uint32_t i=0; i<I2S_FIFO_HALF_DEPTH;) {
|
||||
pcm[i++] = i2s_handle->I2Sx->DATA_L;
|
||||
}
|
||||
}
|
||||
|
||||
hw->wr_ptr += I2S_FIFO_HALF_DEPTH;
|
||||
if (hw->wr_ptr >= hw->pcm_samples) {
|
||||
hw->wr_ptr = 0;
|
||||
}
|
||||
|
||||
/* notify receivers new data are available */
|
||||
audio_hw_output_t *output;
|
||||
output = (void *)co_list_pick(&hw->output_list);
|
||||
while (output) {
|
||||
if (output->handler) {
|
||||
output->handler(I2S_FIFO_HALF_DEPTH);
|
||||
}
|
||||
|
||||
output = (void *)output->hdr.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void i2s_tx_callback(I2S_HandleTypeDef *i2s_handle)
|
||||
{
|
||||
uint8_t index;
|
||||
audio_hw_t *hw;
|
||||
|
||||
for (index=0; index<3; index++) {
|
||||
if (i2s_hw_table[index].hw_handle == i2s_handle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index < 3) {
|
||||
hw = i2s_hw_table[index].audio_hw;
|
||||
|
||||
/* request new data to send through I2S */
|
||||
if (hw->request_handler) {
|
||||
hw->request_handler(hw->pcm_out, I2S_FIFO_HALF_DEPTH, hw->channels);
|
||||
}
|
||||
else {
|
||||
memset(hw->pcm_out, 0, hw->channels * sizeof(int16_t) * I2S_FIFO_HALF_DEPTH);
|
||||
}
|
||||
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||||
for (uint32_t i=0; i<I2S_FIFO_HALF_DEPTH;) {
|
||||
i2s_handle->I2Sx->DATA_L = hw->pcm_out[i++];
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint32_t *pcm = (void *)&hw->pcm_out[0];
|
||||
for (uint32_t i=0; i<I2S_FIFO_HALF_DEPTH;) {
|
||||
i2s_handle->I2Sx->DATA_L = pcm[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void codec_irq(void)
|
||||
{
|
||||
uint32_t status;
|
||||
audio_hw_t *hw;
|
||||
|
||||
status = __CODEC_GET_INT_STATUS();
|
||||
|
||||
if (status & (CODEC_INT_ADCFF_L_AFULL | CODEC_INT_ADCFF_L_FULL)) {
|
||||
// fputc('R', &__stdout);
|
||||
hw = codec_hw.audio_hw;
|
||||
|
||||
/* store data into internal buffer */
|
||||
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||||
int16_t *pcm = (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr];
|
||||
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||||
pcm[i++] = CODEC->ADCDataL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint16_t *pcm = (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr];
|
||||
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||||
uint16_t data = CODEC->ADCDataL;
|
||||
*pcm++ = data;
|
||||
*pcm++ = data;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
hw->wr_ptr += CODEC_FIFO_HALF_DEPTH;
|
||||
if (hw->wr_ptr >= hw->pcm_samples) {
|
||||
hw->wr_ptr = 0;
|
||||
}
|
||||
|
||||
/* notify receivers new data are available */
|
||||
audio_hw_output_t *output;
|
||||
output = (void *)co_list_pick(&hw->output_list);
|
||||
while (output) {
|
||||
if (output->handler) {
|
||||
output->handler(CODEC_FIFO_HALF_DEPTH);
|
||||
}
|
||||
|
||||
output = (void *)output->hdr.next;
|
||||
}
|
||||
}
|
||||
|
||||
if (status & (CODEC_INT_DACFF_L_AEMPTY | CODEC_INT_DACFF_L_EMPTY)) {
|
||||
// fputc('T', &__stdout);
|
||||
hw = codec_hw.audio_hw;
|
||||
|
||||
/* request new data to send through Codec */
|
||||
if (hw->request_handler) {
|
||||
hw->request_handler(hw->pcm_out, CODEC_FIFO_HALF_DEPTH, hw->channels);
|
||||
}
|
||||
else {
|
||||
memset(hw->pcm_out, 0, hw->channels * sizeof(int16_t) * CODEC_FIFO_HALF_DEPTH);
|
||||
}
|
||||
|
||||
#if 1
|
||||
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||||
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||||
CODEC->DACLRDataL = hw->pcm_out[i++];
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint32_t *pcm = (void *)&hw->pcm_out[0];
|
||||
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||||
CODEC->DACLRDataL = pcm[i++];
|
||||
}
|
||||
}
|
||||
#else
|
||||
const int16_t sin_pcm[] = {0, 4000, 8000, 12000, 16000, 20000, 24000, 28000, 32000, 28000, 24000, 20000, 16000, 12000, 8000, 4000,
|
||||
0, -4000, -8000, -12000, -16000, -20000, -24000, -28000, -32000, -28000, -24000, -20000, -16000, -12000, -8000, -4000};
|
||||
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||||
CODEC->DACLRDataL = sin_pcm[i];
|
||||
CODEC->DACLRDataR = sin_pcm[i++];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void pdm_rx_callback(PDM_HandleTypeDef *pdm_handle)
|
||||
{
|
||||
uint8_t index;
|
||||
audio_hw_t *hw;
|
||||
|
||||
for (index=0; index<3; index++) {
|
||||
if (pdm_hw_table[index].hw_handle == pdm_handle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index < 3) {
|
||||
hw = pdm_hw_table[index].audio_hw;
|
||||
|
||||
/* store data into internal buffer */
|
||||
pdm_read_data(pdm_handle, (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr]);
|
||||
hw->wr_ptr += AUDIO_HW_PDM_RX_INT_LEVEL;
|
||||
if (hw->wr_ptr >= hw->pcm_samples) {
|
||||
hw->wr_ptr = 0;
|
||||
}
|
||||
|
||||
/* notify receivers new data are available */
|
||||
audio_hw_output_t *output;
|
||||
output = (void *)co_list_pick(&hw->output_list);
|
||||
while (output) {
|
||||
if (output->handler) {
|
||||
output->handler(AUDIO_HW_PDM_RX_INT_LEVEL);
|
||||
}
|
||||
|
||||
output = (void *)output->hdr.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void i2s0_irq(void)
|
||||
{
|
||||
// fputc('I', NULL);
|
||||
i2s_IRQHandler(i2s_hw_table[0].hw_handle);
|
||||
}
|
||||
|
||||
void i2s1_irq(void)
|
||||
{
|
||||
i2s_IRQHandler(i2s_hw_table[1].hw_handle);
|
||||
}
|
||||
|
||||
void i2s2_irq(void)
|
||||
{
|
||||
i2s_IRQHandler(i2s_hw_table[2].hw_handle);
|
||||
}
|
||||
|
||||
void pdm0_irq(void)
|
||||
{
|
||||
pdm_IRQHandler(pdm_hw_table[0].hw_handle);
|
||||
}
|
||||
|
||||
void pdm1_irq(void)
|
||||
{
|
||||
pdm_IRQHandler(pdm_hw_table[1].hw_handle);
|
||||
}
|
||||
|
||||
void pdm2_irq(void)
|
||||
{
|
||||
pdm_IRQHandler(pdm_hw_table[2].hw_handle);
|
||||
}
|
||||
|
||||
audio_hw_t *audio_hw_create(audio_hw_type_t type, audio_hw_request_pcm_t handler, uint32_t base_addr, audio_hw_dir_t dir, uint32_t sample_rate, uint8_t channels)
|
||||
{
|
||||
audio_hw_t *tmp;
|
||||
bool reject = false;
|
||||
|
||||
/* check duplication */
|
||||
tmp = (void *)co_list_pick(&audio_hw_env.hw_list);
|
||||
while (tmp) {
|
||||
if (type == tmp->type) {
|
||||
reject = true;
|
||||
}
|
||||
tmp = (void *)tmp->hdr.next;
|
||||
}
|
||||
if (reject) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tmp = pvPortMalloc(sizeof(audio_hw_t));
|
||||
if (tmp == NULL) {
|
||||
goto __err;
|
||||
}
|
||||
tmp->type = type;
|
||||
tmp->dir = dir;
|
||||
tmp->channels = channels;
|
||||
tmp->sample_rate = sample_rate;
|
||||
tmp->base_addr = base_addr;
|
||||
tmp->hw_handle = NULL;
|
||||
tmp->request_handler = handler;
|
||||
tmp->pcm_out = NULL;
|
||||
tmp->wr_ptr = 0;
|
||||
co_list_init(&tmp->output_list);
|
||||
tmp->pcm = NULL;
|
||||
|
||||
switch (type) {
|
||||
case AUDIO_HW_TYPE_I2S:
|
||||
{
|
||||
I2S_HandleTypeDef *i2s_handle = pvPortMalloc(sizeof(I2S_HandleTypeDef));
|
||||
if (i2s_handle == NULL) {
|
||||
goto __err;
|
||||
}
|
||||
tmp->hw_handle = i2s_handle;
|
||||
|
||||
if (sample_rate == 44100) {
|
||||
__SYSTEM_I2S_CLK_SELECT_AUPLL();
|
||||
__SYSTEM_I2S_CLK_DIV(8);
|
||||
}
|
||||
else {
|
||||
__SYSTEM_I2S_CLK_SELECT_COREH();
|
||||
__SYSTEM_I2S_CLK_DIV(1);
|
||||
}
|
||||
|
||||
switch (base_addr) {
|
||||
case I2S0_BASE:
|
||||
__SYSTEM_I2S0_CLK_ENABLE();
|
||||
i2s_handle->I2Sx = I2S0;
|
||||
i2s_hw_table[0].hw_handle = i2s_handle;
|
||||
i2s_hw_table[0].audio_hw = tmp;
|
||||
break;
|
||||
case I2S1_BASE:
|
||||
__SYSTEM_I2S1_CLK_ENABLE();
|
||||
i2s_handle->I2Sx = I2S1;
|
||||
i2s_hw_table[1].hw_handle = i2s_handle;
|
||||
i2s_hw_table[1].audio_hw = tmp;
|
||||
break;
|
||||
case I2S2_BASE:
|
||||
__SYSTEM_I2S2_CLK_ENABLE();
|
||||
i2s_handle->I2Sx = I2S2;
|
||||
i2s_hw_table[2].hw_handle = i2s_handle;
|
||||
i2s_hw_table[2].audio_hw = tmp;
|
||||
break;
|
||||
default:
|
||||
goto __err;
|
||||
}
|
||||
|
||||
i2s_handle->Init.Mode = I2S_MODE_MASTER;
|
||||
i2s_handle->Init.Standard = I2S_STANDARD_PHILIPS;
|
||||
i2s_handle->Init.DataFormat = I2S_DATA_FORMAT_16BIT;
|
||||
if (sample_rate == 48000) {
|
||||
i2s_handle->Init.BCLKDIV = 5;
|
||||
i2s_handle->Init.ChannelLength = 50; /* nearly 42K based on 3M BCLK, just for test */
|
||||
i2s_handle->Init.AudioFreq = I2S_AUDIOFREQ_48000;
|
||||
}
|
||||
else if (sample_rate == 44100) {
|
||||
i2s_handle->Init.BCLKDIV = 8;
|
||||
i2s_handle->Init.ChannelLength = 35; /* nearly 42K based on 3M BCLK, just for test */
|
||||
i2s_handle->Init.AudioFreq = I2S_AUDIOFREQ_44100;
|
||||
}
|
||||
else if (sample_rate == 16000) {
|
||||
i2s_handle->Init.BCLKDIV = 15;
|
||||
i2s_handle->Init.ChannelLength = 50; /* nearly 42K based on 3M BCLK, just for test */
|
||||
i2s_handle->Init.AudioFreq = I2S_AUDIOFREQ_16000;
|
||||
}
|
||||
else {
|
||||
goto __err;
|
||||
}
|
||||
|
||||
/* insert new audio hw to list before enable interrupt */
|
||||
co_list_push_back(&audio_hw_env.hw_list, &tmp->hdr);
|
||||
|
||||
/* Initialize and start I2S */
|
||||
i2s_init(i2s_handle);
|
||||
if (dir & AUDIO_HW_DIR_IN) {
|
||||
tmp->pcm_samples = I2S_FIFO_HALF_DEPTH * AUDIO_HW_STORE_FRAME_COUNT;
|
||||
tmp->pcm = (void *)pvPortMalloc(sizeof(int16_t) * channels * tmp->pcm_samples);
|
||||
if (tmp->pcm == NULL) {
|
||||
goto __err;
|
||||
}
|
||||
|
||||
i2s_handle->RxIntCallback = i2s_rx_callback;
|
||||
i2s_receive_IT(i2s_handle);
|
||||
}
|
||||
if (dir & AUDIO_HW_DIR_OUT) {
|
||||
tmp->pcm_out = (void *)pvPortMalloc(sizeof(int16_t) * channels * I2S_FIFO_HALF_DEPTH);
|
||||
if (tmp->pcm_out == NULL) {
|
||||
goto __err;
|
||||
}
|
||||
|
||||
i2s_handle->TxIntCallback = i2s_tx_callback;
|
||||
i2s_transmit_IT(i2s_handle);
|
||||
}
|
||||
|
||||
switch (base_addr) {
|
||||
case I2S0_BASE:
|
||||
NVIC_EnableIRQ(I2S0_IRQn);
|
||||
break;
|
||||
case I2S1_BASE:
|
||||
NVIC_EnableIRQ(I2S1_IRQn);
|
||||
break;
|
||||
case I2S2_BASE:
|
||||
NVIC_EnableIRQ(I2S2_IRQn);
|
||||
break;
|
||||
default:
|
||||
goto __err;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AUDIO_HW_TYPE_CODEC:
|
||||
{
|
||||
codec_hw.hw_handle = pvPortMalloc(sizeof(CODEC_HandleTypeDef));
|
||||
CODEC_HandleTypeDef *CODEC_h = codec_hw.hw_handle;
|
||||
codec_hw.audio_hw = tmp;
|
||||
tmp->hw_handle = CODEC_h;
|
||||
if (dir & AUDIO_HW_DIR_IN) {
|
||||
tmp->pcm_samples = CODEC_FIFO_HALF_DEPTH * AUDIO_HW_STORE_FRAME_COUNT;
|
||||
tmp->pcm = (void *)pvPortMalloc(sizeof(int16_t) * channels * tmp->pcm_samples);
|
||||
if (tmp->pcm == NULL) {
|
||||
goto __err;
|
||||
}
|
||||
}
|
||||
if (dir & AUDIO_HW_DIR_OUT) {
|
||||
tmp->pcm_out = (void *)pvPortMalloc(sizeof(int16_t) * channels * CODEC_FIFO_HALF_DEPTH);
|
||||
if (tmp->pcm_out == NULL) {
|
||||
goto __err;
|
||||
}
|
||||
}
|
||||
|
||||
__SYSTEM_CODEC_CLK_ENABLE();
|
||||
CODEC->CodecAna0 = 0x00004080;
|
||||
CODEC->CodecAna1 = 0x47628100;
|
||||
CODEC->CodecAna2 = 0x000d1FCF;
|
||||
CODEC->CodecAna3 = 0x04000000;
|
||||
CODEC_h->Init.Codec_Input_sel = INPUT_SELECT_ADC;
|
||||
CODEC_h->Init.Codec_Input_Routing_sel = INPUT_ROUTING_ASRC0_GPF0_ALC_ADCFF;
|
||||
|
||||
CODEC_h->Init.Codec_Output_sel = OUTPUT_SELECT_DAC;
|
||||
CODEC_h->Init.Codec_Output_Routing_sel = OUTPUT_ROUTING_DACFFLR_ASRC1_GPF1_DRC;
|
||||
CODEC_h->Init.ADC_DataFormat = CODEC_FIFO_FORMAT_16BIT;
|
||||
CODEC_h->Init.DAC_LR_DataFormat = CODEC_FIFO_FORMAT_16BIT;
|
||||
CODEC_h->Init.DAC_01_DataFormat = CODEC_FIFO_FORMAT_16BIT;
|
||||
|
||||
if (dir & AUDIO_HW_DIR_IN) {
|
||||
CODEC_h->Init.Codec_Input_sel = INPUT_SELECT_ADC;
|
||||
CODEC_h->InputConfig.ADC_Oversampling_Level = CODEC_OVERSAMPLING_HIGH;
|
||||
if (sample_rate == 48000) {
|
||||
CODEC_h->InputConfig.ADC_SampleRate = CODEC_SAMPLE_RATE_48000;
|
||||
CODEC_h->InputConfig.ADC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||||
}
|
||||
else if (sample_rate == 44100) {
|
||||
CODEC_h->InputConfig.ADC_SampleRate = CODEC_SAMPLE_RATE_44100;
|
||||
CODEC_h->InputConfig.ADC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||||
}
|
||||
else if (sample_rate == 16000) {
|
||||
CODEC_h->InputConfig.ADC_SampleRate = CODEC_SAMPLE_RATE_16000;
|
||||
CODEC_h->InputConfig.ADC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||||
}
|
||||
else if (sample_rate == 8000) {
|
||||
CODEC_h->InputConfig.ADC_SampleRate = CODEC_SAMPLE_RATE_8000;
|
||||
CODEC_h->InputConfig.ADC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||||
}
|
||||
else {
|
||||
goto __err;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CODEC_h->Init.Codec_Input_sel = INPUT_SELECT_BYPASS;
|
||||
}
|
||||
|
||||
if (dir & AUDIO_HW_DIR_OUT) {
|
||||
CODEC_h->Init.Codec_Output_sel = OUTPUT_SELECT_DAC;
|
||||
CODEC_h->OutputConfig.DAC_Oversampling_Level = CODEC_OVERSAMPLING_HIGH;
|
||||
if (sample_rate == 48000) {
|
||||
CODEC_h->OutputConfig.DAC_SampleRate = CODEC_SAMPLE_RATE_48000;
|
||||
CODEC_h->OutputConfig.DAC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||||
}
|
||||
else if (sample_rate == 44100) {
|
||||
CODEC_h->OutputConfig.DAC_SampleRate = CODEC_SAMPLE_RATE_44100;
|
||||
CODEC_h->OutputConfig.DAC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||||
}
|
||||
else if (sample_rate == 16000) {
|
||||
CODEC_h->OutputConfig.DAC_SampleRate = CODEC_SAMPLE_RATE_16000;
|
||||
CODEC_h->OutputConfig.DAC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||||
}
|
||||
else if (sample_rate == 8000) {
|
||||
CODEC_h->OutputConfig.DAC_SampleRate = CODEC_SAMPLE_RATE_8000;
|
||||
CODEC_h->OutputConfig.DAC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||||
}
|
||||
else {
|
||||
goto __err;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CODEC_h->Init.Codec_Output_sel = OUTPUT_SELECT_BYPASS;
|
||||
}
|
||||
|
||||
codec_init(CODEC_h);
|
||||
__CODEC_ADCFF_L_FULL_THRESHOLD(32);
|
||||
__CODEC_ADCFF_R_FULL_THRESHOLD(32);
|
||||
if (dir & AUDIO_HW_DIR_IN) {
|
||||
codec_int_enable(CODEC_INT_ADCFF_L_FULL | CODEC_INT_ADCFF_L_AFULL);
|
||||
}
|
||||
if (dir & AUDIO_HW_DIR_OUT) {
|
||||
codec_int_enable(CODEC_INT_DACFF_L_EMPTY | CODEC_INT_DACFF_L_AEMPTY);
|
||||
}
|
||||
codec_Set_ADC_Volume(0x8000);
|
||||
codec_Set_DAC_Volume(0x0800);
|
||||
codec_mute_output(1);
|
||||
|
||||
/* insert new audio hw to list before enable interrupt */
|
||||
co_list_push_back(&audio_hw_env.hw_list, &tmp->hdr);
|
||||
|
||||
NVIC_EnableIRQ(CODEC_IRQn);
|
||||
}
|
||||
break;
|
||||
case AUDIO_HW_TYPE_PDM:
|
||||
{
|
||||
if (dir & AUDIO_HW_DIR_INOUT) {
|
||||
goto __err;
|
||||
}
|
||||
|
||||
PDM_HandleTypeDef *pdm_handle = pvPortMalloc(sizeof(PDM_HandleTypeDef));
|
||||
if (pdm_handle == NULL) {
|
||||
goto __err;
|
||||
}
|
||||
tmp->hw_handle = pdm_handle;
|
||||
tmp->pcm_samples = AUDIO_HW_PDM_RX_INT_LEVEL * AUDIO_HW_STORE_FRAME_COUNT;
|
||||
tmp->pcm = (void *)pvPortMalloc(sizeof(int16_t) * channels * tmp->pcm_samples);
|
||||
if (tmp->pcm == NULL) {
|
||||
goto __err;
|
||||
}
|
||||
|
||||
switch (base_addr) {
|
||||
case PDM0_BASE:
|
||||
__SYSTEM_PDM0_CLK_ENABLE();
|
||||
pdm_handle->PDMx = PDM0;
|
||||
pdm_hw_table[0].hw_handle = pdm_handle;
|
||||
pdm_hw_table[0].audio_hw = tmp;
|
||||
break;
|
||||
case PDM1_BASE:
|
||||
__SYSTEM_PDM1_CLK_ENABLE();
|
||||
pdm_handle->PDMx = PDM1;
|
||||
pdm_hw_table[1].hw_handle = pdm_handle;
|
||||
pdm_hw_table[1].audio_hw = tmp;
|
||||
break;
|
||||
case PDM2_BASE:
|
||||
__SYSTEM_PDM2_CLK_ENABLE();
|
||||
pdm_handle->PDMx = PDM2;
|
||||
pdm_hw_table[2].hw_handle = pdm_handle;
|
||||
pdm_hw_table[2].audio_hw = tmp;
|
||||
break;
|
||||
default:
|
||||
goto __err;
|
||||
}
|
||||
|
||||
if (sample_rate == 48000) {
|
||||
pdm_handle->Init.SampleRate = PDM_SAMPLE_RATE_48000;
|
||||
}
|
||||
else if (sample_rate == 44100) {
|
||||
pdm_handle->Init.SampleRate = PDM_SAMPLE_RATE_44100;
|
||||
}
|
||||
else if (sample_rate == 16000) {
|
||||
pdm_handle->Init.SampleRate = PDM_SAMPLE_RATE_16000;
|
||||
}
|
||||
else {
|
||||
goto __err;
|
||||
}
|
||||
pdm_handle->Init.OverSampleMode = PDM_OSM_0;
|
||||
if (channels == AUDIO_CHANNELS_MONO) {
|
||||
pdm_handle->Init.ChannelMode = PDM_MONO_LEFT;
|
||||
}
|
||||
else if (channels == AUDIO_CHANNELS_STEREO) {
|
||||
pdm_handle->Init.ChannelMode = PDM_STEREO;
|
||||
}
|
||||
else {
|
||||
goto __err;
|
||||
}
|
||||
pdm_handle->Init.Volume = 0;
|
||||
pdm_handle->Init.FIFO_FullThreshold = AUDIO_HW_PDM_RX_INT_LEVEL;
|
||||
|
||||
pdm_handle->p_RxData = NULL;
|
||||
pdm_handle->RxCallback = pdm_rx_callback;
|
||||
|
||||
/* insert new audio hw to list before enable interrupt */
|
||||
co_list_push_back(&audio_hw_env.hw_list, &tmp->hdr);
|
||||
|
||||
pdm_init(pdm_handle);
|
||||
pdm_start_IT(pdm_handle, NULL);
|
||||
switch (base_addr) {
|
||||
case PDM0_BASE:
|
||||
NVIC_EnableIRQ(PDM0_IRQn);
|
||||
break;
|
||||
case PDM1_BASE:
|
||||
NVIC_EnableIRQ(PDM1_IRQn);
|
||||
break;
|
||||
case PDM2_BASE:
|
||||
NVIC_EnableIRQ(PDM2_IRQn);
|
||||
break;
|
||||
default:
|
||||
goto __err;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto __err;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
|
||||
__err:
|
||||
if (tmp->hw_handle) {
|
||||
vPortFree(tmp->hw_handle);
|
||||
}
|
||||
if (tmp->pcm) {
|
||||
vPortFree(tmp->pcm);
|
||||
}
|
||||
if (tmp->pcm_out) {
|
||||
vPortFree(tmp->pcm_out);
|
||||
}
|
||||
co_list_extract(&audio_hw_env.hw_list, &tmp->hdr);
|
||||
vPortFree(tmp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void audio_hw_destroy(audio_hw_t *hw)
|
||||
{
|
||||
if (hw == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLOBAL_INT_DISABLE();
|
||||
if (co_list_extract(&audio_hw_env.hw_list, &hw->hdr)) {
|
||||
switch (hw->type) {
|
||||
case AUDIO_HW_TYPE_I2S:
|
||||
i2s_deinit(hw->hw_handle);
|
||||
switch (hw->base_addr) {
|
||||
case I2S0_BASE:
|
||||
__SYSTEM_I2S0_CLK_DISABLE();
|
||||
NVIC_DisableIRQ(I2S0_IRQn);
|
||||
break;
|
||||
case I2S1_BASE:
|
||||
__SYSTEM_I2S1_CLK_DISABLE();
|
||||
NVIC_DisableIRQ(I2S1_IRQn);
|
||||
break;
|
||||
case I2S2_BASE:
|
||||
__SYSTEM_I2S2_CLK_DISABLE();
|
||||
NVIC_DisableIRQ(I2S2_IRQn);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AUDIO_HW_TYPE_CODEC:
|
||||
codec_deinit(hw->hw_handle);
|
||||
__SYSTEM_CODEC_CLK_DISABLE();
|
||||
NVIC_DisableIRQ(CODEC_IRQn);
|
||||
break;
|
||||
case AUDIO_HW_TYPE_PDM:
|
||||
pdm_stop(hw->hw_handle);
|
||||
switch (hw->base_addr) {
|
||||
case PDM0_BASE:
|
||||
__SYSTEM_PDM0_CLK_DISABLE();
|
||||
NVIC_DisableIRQ(PDM0_IRQn);
|
||||
break;
|
||||
case PDM1_BASE:
|
||||
__SYSTEM_PDM1_CLK_DISABLE();
|
||||
NVIC_DisableIRQ(PDM1_IRQn);
|
||||
break;
|
||||
case PDM2_BASE:
|
||||
__SYSTEM_PDM2_CLK_DISABLE();
|
||||
NVIC_DisableIRQ(PDM2_IRQn);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
GLOBAL_INT_RESTORE();
|
||||
|
||||
audio_hw_output_t *output;
|
||||
output = (void *)co_list_pop_front(&hw->output_list);
|
||||
while (output) {
|
||||
vPortFree(output);
|
||||
output = (void *)co_list_pop_front(&hw->output_list);
|
||||
}
|
||||
|
||||
if (hw->hw_handle) {
|
||||
vPortFree(hw->hw_handle);
|
||||
}
|
||||
if (hw->pcm) {
|
||||
vPortFree(hw->pcm);
|
||||
}
|
||||
if (hw->pcm_out) {
|
||||
vPortFree(hw->pcm_out);
|
||||
}
|
||||
vPortFree(hw);
|
||||
}
|
||||
|
||||
audio_hw_output_t *audio_hw_output_add(audio_hw_t *hw, audio_hw_receive_pcm_ntf_t handler)
|
||||
{
|
||||
audio_hw_output_t *output;
|
||||
|
||||
if (hw == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output = pvPortMalloc(sizeof(audio_hw_output_t));
|
||||
if (output) {
|
||||
GLOBAL_INT_DISABLE();
|
||||
output->audio_hw = hw;
|
||||
output->handler = handler;
|
||||
output->rd_ptr = hw->wr_ptr;
|
||||
co_list_push_back(&hw->output_list, &output->hdr);
|
||||
GLOBAL_INT_RESTORE();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void audio_hw_output_remove(audio_hw_t *hw, audio_hw_output_t *output)
|
||||
{
|
||||
if (hw == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLOBAL_INT_DISABLE();
|
||||
co_list_extract(&hw->output_list, &output->hdr);
|
||||
GLOBAL_INT_RESTORE();
|
||||
|
||||
vPortFree(output);
|
||||
}
|
||||
|
||||
static void *copy_pcm(audio_hw_output_t *output, void *pcm, uint32_t samples, uint8_t channels)
|
||||
{
|
||||
audio_hw_t *hw = output->audio_hw;
|
||||
|
||||
if (channels == AUDIO_CHANNELS_MONO) {
|
||||
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||||
int16_t *src = (int16_t *)hw->pcm + output->rd_ptr;
|
||||
int16_t *dst = pcm;
|
||||
for (uint32_t i=0; i<samples; i++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
pcm = dst;
|
||||
}
|
||||
else {
|
||||
int16_t *src = (int16_t *)hw->pcm + (output->rd_ptr << 1);
|
||||
int16_t *dst = pcm;
|
||||
for (uint32_t i=0; i<samples; i++) {
|
||||
*dst++ = *src;
|
||||
src += 2;
|
||||
}
|
||||
pcm = dst;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||||
int16_t *src = (int16_t *)hw->pcm + output->rd_ptr;
|
||||
int16_t *dst = pcm;
|
||||
for (uint32_t i=0; i<samples; i++) {
|
||||
*dst++ = *src;
|
||||
*dst++ = *src++;
|
||||
}
|
||||
pcm = dst;
|
||||
}
|
||||
else {
|
||||
uint32_t *src = (uint32_t *)hw->pcm + output->rd_ptr;
|
||||
uint32_t *dst = pcm;
|
||||
for (uint32_t i=0; i<samples; i++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
pcm = dst;
|
||||
}
|
||||
}
|
||||
|
||||
return pcm;
|
||||
}
|
||||
|
||||
uint32_t audio_hw_read_pcm(audio_hw_output_t *output, void *pcm, uint32_t samples, uint8_t channels)
|
||||
{
|
||||
audio_hw_t *hw = output->audio_hw;
|
||||
uint32_t tail_samples;
|
||||
|
||||
tail_samples = hw->pcm_samples - output->rd_ptr;
|
||||
if (tail_samples <= samples) {
|
||||
pcm = copy_pcm(output, pcm, tail_samples, channels);
|
||||
output->rd_ptr = 0;
|
||||
samples -= tail_samples;
|
||||
}
|
||||
if (samples) {
|
||||
pcm = copy_pcm(output, pcm, samples, channels);
|
||||
output->rd_ptr += samples;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
62
MCU/components/modules/audio/audio_hw.h
Normal file
62
MCU/components/modules/audio/audio_hw.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef _AUDIO_HW_H
|
||||
#define _AUDIO_HW_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "co_list.h"
|
||||
|
||||
typedef void (*audio_hw_request_pcm_t)(void *pcm, uint32_t samples, uint8_t channels);
|
||||
typedef void (*audio_hw_receive_pcm_ntf_t)(uint32_t samples);
|
||||
|
||||
typedef enum {
|
||||
AUDIO_HW_TYPE_I2S,
|
||||
AUDIO_HW_TYPE_CODEC,
|
||||
AUDIO_HW_TYPE_PDM,
|
||||
} audio_hw_type_t;
|
||||
|
||||
typedef enum {
|
||||
AUDIO_HW_DIR_IN = 0x01,
|
||||
AUDIO_HW_DIR_OUT = 0x02,
|
||||
AUDIO_HW_DIR_INOUT = (AUDIO_HW_DIR_IN | AUDIO_HW_DIR_OUT),
|
||||
} audio_hw_dir_t;
|
||||
|
||||
typedef struct {
|
||||
struct co_list_hdr hdr;
|
||||
|
||||
audio_hw_type_t type;
|
||||
audio_hw_dir_t dir;
|
||||
uint8_t channels;
|
||||
uint32_t sample_rate;
|
||||
uint32_t base_addr;
|
||||
void *hw_handle;
|
||||
|
||||
/* used for output mode */
|
||||
audio_hw_request_pcm_t request_handler;
|
||||
int16_t *pcm_out;
|
||||
|
||||
/* used for input mode */
|
||||
uint32_t wr_ptr; /* unit is sample */
|
||||
struct co_list output_list;
|
||||
uint32_t pcm_samples; /* unit is sample */
|
||||
uint8_t *pcm;
|
||||
} audio_hw_t;
|
||||
|
||||
typedef struct {
|
||||
struct co_list_hdr hdr;
|
||||
|
||||
audio_hw_t *audio_hw;
|
||||
audio_hw_receive_pcm_ntf_t handler;
|
||||
uint32_t rd_ptr; /* unit is sample */
|
||||
} audio_hw_output_t;
|
||||
|
||||
audio_hw_t *audio_hw_create(audio_hw_type_t type, audio_hw_request_pcm_t handler, uint32_t base_addr, audio_hw_dir_t dir, uint32_t sample_rate, uint8_t channels);
|
||||
|
||||
void audio_hw_destroy(audio_hw_t *hw);
|
||||
|
||||
audio_hw_output_t *audio_hw_output_add(audio_hw_t *hw, audio_hw_receive_pcm_ntf_t handler);
|
||||
|
||||
void audio_hw_output_remove(audio_hw_t *hw, audio_hw_output_t *output);
|
||||
|
||||
uint32_t audio_hw_read_pcm(audio_hw_output_t *output, void *pcm, uint32_t samples, uint8_t channels);
|
||||
|
||||
#endif // _AUDIO_HW_H
|
0
MCU/components/modules/audio/audio_rpmsg.c
Normal file
0
MCU/components/modules/audio/audio_rpmsg.c
Normal file
114
MCU/components/modules/audio/audio_rpmsg.h
Normal file
114
MCU/components/modules/audio/audio_rpmsg.h
Normal file
@ -0,0 +1,114 @@
|
||||
#ifndef _AUDIO_RPMSG_H
|
||||
#define _AUDIO_RPMSG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rpmsg.h"
|
||||
|
||||
#define RPMSG_SYNC_FUNC_DEC_INIT RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0001)
|
||||
#define RPMSG_SYNC_FUNC_DEC_DESTROY RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0002)
|
||||
#define RPMSG_SYNC_FUNC_DEC_EXEC RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0003)
|
||||
#define RPMSG_SYNC_FUNC_DEC_PLC RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0004)
|
||||
#define RPMSG_SYNC_FUNC_DEC_GET_PARAM RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0005)
|
||||
#define RPMSG_SYNC_FUNC_ENC_INIT RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0006)
|
||||
#define RPMSG_SYNC_FUNC_ENC_DESTROY RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0007)
|
||||
#define RPMSG_SYNC_FUNC_ENC_EXEC RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0008)
|
||||
#define RPMSG_SYNC_FUNC_RESAMPLE_INIT RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0009)
|
||||
#define RPMSG_SYNC_FUNC_RESAMPLE_EXEC RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x000a)
|
||||
#define RPMSG_SYNC_FUNC_RESAMPLE_DESTROY RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x000b)
|
||||
#define RPMSG_SYNC_FUNC_AUDIO_ALGO_INIT RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x000c)
|
||||
#define RPMSG_SYNC_FUNC_AUDIO_ALGO_LAUNCH RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x000d)
|
||||
#define RPMSG_SYNC_FUNC_AUDIO_ALGO_RELEASE RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x000e)
|
||||
#define RPMSG_SYNC_FUNC_VOICE_RECOGNIZE_INIT RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x000f)
|
||||
#define RPMSG_SYNC_FUNC_VOICE_RECOGNIZE_LAUNCH RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0010)
|
||||
#define RPMSG_SYNC_FUNC_VOICE_RECOGNIZE_RELEASE RPMSG_SYNC_FUNC_MSG(RPMSG_SYNC_FUNC_TYPE_AUDIO, 0x0011)
|
||||
|
||||
struct rpmsg_sync_msg_decoder_init_t {
|
||||
uint8_t decoder_type;
|
||||
void *param;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_decoder_exec_t {
|
||||
void *handle;
|
||||
const uint8_t *in_buffer;
|
||||
uint32_t *in_length;
|
||||
uint8_t **out_buffer;
|
||||
uint32_t *out_length;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_decoder_plc_t {
|
||||
void *handle;
|
||||
uint8_t **out_buffer;
|
||||
uint32_t *out_length;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_decoder_get_param_t {
|
||||
void *handle;
|
||||
uint32_t *sample_rate;
|
||||
uint8_t *channels;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_decoder_destroy_t {
|
||||
void *handle;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_encoder_init_t {
|
||||
uint8_t encoder_type;
|
||||
void *param;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_encoder_exec_t {
|
||||
void *handle;
|
||||
const uint8_t *in_buffer;
|
||||
uint32_t *in_length;
|
||||
uint8_t **out_buffer;
|
||||
uint32_t *out_length;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_encoder_destroy_t {
|
||||
void *handle;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_resample_init_t {
|
||||
uint8_t resample_type;
|
||||
uint8_t channels;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_resample_exec_t {
|
||||
void *handle;
|
||||
const uint8_t *in_buffer;
|
||||
uint32_t *in_length;
|
||||
uint8_t **out_buffer;
|
||||
uint32_t *out_length;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_resample_destroy_t {
|
||||
void *handle;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_algorithm_init {
|
||||
uint8_t algo_sel;
|
||||
uint32_t sample_rate;
|
||||
uint8_t ns_level;
|
||||
uint16_t agc_mode;
|
||||
uint32_t *frame_size;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_algorithm_launch {
|
||||
void *handle;
|
||||
const int16_t *farend;
|
||||
const int16_t *nearend;
|
||||
int16_t **out;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_algorithm_destroy_t {
|
||||
void *handle;
|
||||
};
|
||||
|
||||
struct rpmsg_sync_msg_voice_recognize_launch_t {
|
||||
const int16_t *mic_pcm;
|
||||
uint32_t samples;
|
||||
};
|
||||
|
||||
#endif // _AUDIO_RPMSG_H
|
||||
|
648
MCU/components/modules/audio/audio_scene.c
Normal file
648
MCU/components/modules/audio/audio_scene.c
Normal file
@ -0,0 +1,648 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "audio_scene.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "semphr.h"
|
||||
|
||||
#include "co_list.h"
|
||||
#include "codec.h"
|
||||
#include "algorithm.h"
|
||||
#include "dsp_mem.h"
|
||||
#include "dram_mem.h"
|
||||
|
||||
/*
|
||||
* How to implement a audio scene
|
||||
* 1. define audio_scene_param_xxx_t, add it into audio_scene_t->param
|
||||
* 2. define xxx_env_t, add it into audio_scene_env_t->env
|
||||
* 3. implement audio scene task xxx_task and xxx_init
|
||||
* 4. add corresponding statement in function audio_scene_create
|
||||
* 5. implement destroy_xxx
|
||||
* 6. add corresponding statement in function destroy_task
|
||||
*/
|
||||
|
||||
#define A2DP_SOURCE_SINGLE_FRAME_DUR 10 // unit: ms
|
||||
|
||||
#define AUDIO_ALGO_ADC_IN_BUFFER_FRAME_COUNT 8 // unit: algo_frame_size
|
||||
|
||||
#define DECODER_DATA_READY_THD 6
|
||||
|
||||
audio_scene_env_t audio_scene_env = {0};
|
||||
static SemaphoreHandle_t wait_for_delete;
|
||||
|
||||
static void send_event(audio_scene_evt_t *evt)
|
||||
{
|
||||
if (evt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLOBAL_INT_DISABLE();
|
||||
co_list_push_back(&audio_scene_env.evt_list, &evt->hdr);
|
||||
GLOBAL_INT_RESTORE();
|
||||
|
||||
if(xPortIsInsideInterrupt()) {
|
||||
vTaskNotifyGiveFromISR(audio_scene_env.audio_task_handle, NULL);
|
||||
}
|
||||
else {
|
||||
xTaskNotifyGive(audio_scene_env.audio_task_handle);
|
||||
}
|
||||
}
|
||||
|
||||
/* this function may be called from interrupt, just send message to audio_scene_task */
|
||||
static void decoder_request_raw_data_cb(audio_decoder_t *decoder)
|
||||
{
|
||||
audio_scene_evt_t *evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
|
||||
|
||||
if (evt) {
|
||||
evt->type = AUDIO_SCENE_EVT_TYPE_REQ_RAW_DATA;
|
||||
evt->p.decoder = decoder;
|
||||
send_event(evt);
|
||||
}
|
||||
}
|
||||
|
||||
static void hw_request_a2dp_sink_pcm_cb(void *pcm, uint32_t samples, uint8_t channels)
|
||||
{
|
||||
uint32_t avaliable_data;
|
||||
/* request PCM data from audio decoder */
|
||||
avaliable_data = audio_decoder_get_pcm(audio_scene_env.env.a2dp_sink.decoder_to_hw, pcm, samples, channels);
|
||||
}
|
||||
|
||||
static void hw_request_sco_pcm_cb(void *pcm, uint32_t samples, uint8_t channels)
|
||||
{
|
||||
/* request PCM data by speaker, these data are output of Audio algorithm */
|
||||
audio_decoder_get_pcm(audio_scene_env.env.sco.decoder_to_hw, pcm, samples, channels);
|
||||
}
|
||||
|
||||
static void hw_receive_adc_pcm_cb(uint32_t samples)
|
||||
{
|
||||
audio_scene_evt_t *evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
|
||||
|
||||
if (evt) {
|
||||
evt->type = AUDIO_SCENE_EVT_TYPE_ADC_NEW_SAMPLES;
|
||||
evt->p.adc_new_samples = samples;
|
||||
send_event(evt);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_a2dp_sink(void)
|
||||
{
|
||||
audio_scene_param_a2dp_sink_t *param;
|
||||
|
||||
/* initialize audio decoder module */
|
||||
param = &audio_scene_env.scene->param.a2dp_sink;
|
||||
audio_decoder_init(param->channels, param->sample_rate);
|
||||
|
||||
/* create and start a decoder */
|
||||
audio_scene_env.env.a2dp_sink.decoder = audio_decoder_add(param->audio_type, decoder_request_raw_data_cb);
|
||||
assert(audio_scene_env.env.a2dp_sink.decoder != NULL);
|
||||
audio_decoder_start(audio_scene_env.env.a2dp_sink.decoder);
|
||||
|
||||
/* add an output to initialized audio decoder module */
|
||||
audio_scene_env.env.a2dp_sink.decoder_to_hw = audio_decoder_output_add(true, AUDIO_CHANNELS_STEREO);
|
||||
|
||||
/* initialize audio hardware */
|
||||
audio_scene_env.env.a2dp_sink.audio_hw = audio_hw_create(param->hw_type,
|
||||
hw_request_a2dp_sink_pcm_cb,
|
||||
param->hw_base_addr,
|
||||
AUDIO_HW_DIR_OUT,
|
||||
param->sample_rate,
|
||||
param->channels);
|
||||
|
||||
co_list_init(&audio_scene_env.env.a2dp_sink.a2dp_data_list);
|
||||
audio_scene_env.env.a2dp_sink.a2dp_data_counter = 0;
|
||||
audio_scene_env.env.a2dp_sink.start_thd = DECODER_DATA_READY_THD;
|
||||
}
|
||||
|
||||
static void init_sco(void)
|
||||
{
|
||||
audio_scene_param_sco_t *param;
|
||||
sco_env_t *env;
|
||||
|
||||
env = &audio_scene_env.env.sco;
|
||||
param = &audio_scene_env.scene->param.sco;
|
||||
|
||||
/* create audio algorithm instance */
|
||||
/*
|
||||
enum
|
||||
{
|
||||
kAgcModeUnchanged,
|
||||
kAgcModeAdaptiveAnalog,
|
||||
kAgcModeAdaptiveDigital,
|
||||
kAgcModeFixedDigital
|
||||
};
|
||||
*/
|
||||
env->audio_algo_handle = algorithm_init(/*AUDIO_ALGO_SEL_AGC | AUDIO_ALGO_SEL_NS | */AUDIO_ALGO_SEL_AEC,
|
||||
param->sample_rate,
|
||||
3, 3,
|
||||
&env->algo_frame_size);
|
||||
|
||||
/* request buffers */
|
||||
env->decoder_output = dsp_mem_alloc(env->algo_frame_size * sizeof(int16_t));
|
||||
env->decoder_output_wr_ptr = 0;
|
||||
env->adc_input = dsp_mem_alloc(env->algo_frame_size * sizeof(int16_t) * AUDIO_ALGO_ADC_IN_BUFFER_FRAME_COUNT);
|
||||
env->adc_input_size = env->algo_frame_size * AUDIO_ALGO_ADC_IN_BUFFER_FRAME_COUNT;
|
||||
env->adc_input_wr_ptr = 0;
|
||||
env->adc_input_rd_ptr = 0;
|
||||
|
||||
/* initialize audio decoder module */
|
||||
audio_decoder_init(AUDIO_CHANNELS_MONO, param->sample_rate);
|
||||
|
||||
/* create and start a decoder */
|
||||
env->decoder = audio_decoder_add(param->audio_type, decoder_request_raw_data_cb);
|
||||
audio_decoder_start(env->decoder);
|
||||
|
||||
/* add an output to initialized audio decoder module */
|
||||
env->decoder_to_algo = audio_decoder_output_add(false, AUDIO_CHANNELS_MONO);
|
||||
/* add an output to initialized audio decoder module */
|
||||
env->decoder_to_hw = audio_decoder_output_add(true, AUDIO_CHANNELS_MONO);
|
||||
|
||||
/* create encoder */
|
||||
struct msbc_encoder_param _param;
|
||||
_param.i_bitrate = 128000;
|
||||
_param.i_samp_freq = 16000;
|
||||
if (param->audio_type == AUDIO_TYPE_MSBC) {
|
||||
env->encoder = audio_encoder_init(param->audio_type, AUDIO_CHANNELS_MONO, param->sample_rate, 57, &_param);
|
||||
}
|
||||
else {
|
||||
env->encoder = audio_encoder_init(param->audio_type, AUDIO_CHANNELS_MONO, param->sample_rate, 256, &_param);
|
||||
}
|
||||
|
||||
/* initialize audio hardware */
|
||||
env->audio_hw = audio_hw_create(param->hw_type,
|
||||
hw_request_sco_pcm_cb,
|
||||
param->hw_base_addr,
|
||||
AUDIO_HW_DIR_INOUT,
|
||||
param->sample_rate,
|
||||
AUDIO_CHANNELS_STEREO);
|
||||
env->audio_hw_output = audio_hw_output_add(env->audio_hw, hw_receive_adc_pcm_cb);
|
||||
|
||||
co_list_init(&env->sco_data_list);
|
||||
env->sco_data_counter = 0;
|
||||
env->start_thd = DECODER_DATA_READY_THD;
|
||||
}
|
||||
|
||||
static void destroy_a2dp_sink(void)
|
||||
{
|
||||
if (audio_scene_env.scene
|
||||
&& (audio_scene_env.scene->type == AUDIO_SCENE_TYPE_A2DP_SINK)) {
|
||||
/* release audio hardware */
|
||||
audio_hw_destroy(audio_scene_env.env.a2dp_sink.audio_hw);
|
||||
|
||||
/* release audio decoder module */
|
||||
audio_decoder_destroy();
|
||||
|
||||
audio_data_element_t *elt = (void *)co_list_pop_front(&audio_scene_env.env.a2dp_sink.a2dp_data_list);
|
||||
while (elt) {
|
||||
dram_free(elt);
|
||||
elt = (void *)co_list_pop_front(&audio_scene_env.env.a2dp_sink.a2dp_data_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_sco(void)
|
||||
{
|
||||
if (audio_scene_env.scene
|
||||
&& (audio_scene_env.scene->type == AUDIO_SCENE_TYPE_SCO)) {
|
||||
sco_env_t *env = &audio_scene_env.env.sco;
|
||||
|
||||
/* release audio hardware */
|
||||
audio_hw_destroy(env->audio_hw);
|
||||
|
||||
/* release audio encoder */
|
||||
audio_encoder_destroy(env->encoder);
|
||||
|
||||
/* release audio decoder module */
|
||||
audio_decoder_destroy();
|
||||
|
||||
/* release audio algorithm instance */
|
||||
algorithm_destroy(env->audio_algo_handle);
|
||||
|
||||
/* free allocated buffer */
|
||||
dsp_mem_free(env->decoder_output);
|
||||
dsp_mem_free(env->adc_input);
|
||||
|
||||
audio_data_element_t *elt = (void *)co_list_pop_front(&audio_scene_env.env.sco.sco_data_list);
|
||||
while (elt) {
|
||||
dram_free(elt);
|
||||
elt = (void *)co_list_pop_front(&audio_scene_env.env.sco.sco_data_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void a2dp_sink_evt_handler(audio_scene_evt_t *evt)
|
||||
{
|
||||
audio_scene_t *scene = audio_scene_env.scene;
|
||||
a2dp_sink_env_t *a2dp_sink_env = &audio_scene_env.env.a2dp_sink;
|
||||
|
||||
switch (evt->type) {
|
||||
case AUDIO_SCENE_EVT_TYPE_REQ_RAW_DATA:
|
||||
{
|
||||
uint32_t length = 0;
|
||||
if (evt->p.decoder == a2dp_sink_env->decoder) {
|
||||
if (audio_decoder_decode(a2dp_sink_env->decoder,
|
||||
NULL, &length) != AUDIO_RET_OUTPUT_ALMOTE_FULL) {
|
||||
if (a2dp_sink_env->start_thd == 0) {
|
||||
audio_data_element_t *elt;
|
||||
int ret;
|
||||
elt = (void *)co_list_pick(&a2dp_sink_env->a2dp_data_list);
|
||||
while (elt) {
|
||||
uint32_t length = elt->length - elt->offset;
|
||||
ret = audio_decoder_decode(a2dp_sink_env->decoder, &elt->buffer[elt->offset], &length);
|
||||
elt->offset += length;
|
||||
if (elt->length == elt->offset) {
|
||||
elt = (void *)co_list_pop_front(&a2dp_sink_env->a2dp_data_list);
|
||||
a2dp_sink_env->a2dp_data_counter--;
|
||||
dram_free(elt);
|
||||
elt = (void *)co_list_pick(&a2dp_sink_env->a2dp_data_list);
|
||||
}
|
||||
if (ret == AUDIO_RET_OUTPUT_ALMOTE_FULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (evt->p.decoder == audio_scene_env.tone.decoder) {
|
||||
// fputc('T', NULL);
|
||||
if (audio_decoder_decode(audio_scene_env.tone.decoder,
|
||||
NULL, &length) != AUDIO_RET_OUTPUT_ALMOTE_FULL) {
|
||||
/* send request to app layer after checking PCM list */
|
||||
void (*req_dec_cb)(audio_decoder_t *) = audio_scene_env.tone.req_dec_cb;
|
||||
if (req_dec_cb) {
|
||||
req_dec_cb(audio_scene_env.tone.decoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AUDIO_SCENE_EVT_TYPE_RECV_RAW_DATA:
|
||||
co_list_push_back(&audio_scene_env.env.a2dp_sink.a2dp_data_list, &evt->p.raw_data->hdr);
|
||||
a2dp_sink_env->a2dp_data_counter++;
|
||||
if (a2dp_sink_env->start_thd) {
|
||||
a2dp_sink_env->start_thd--;
|
||||
}
|
||||
break;
|
||||
case AUDIO_SCENE_EVT_TYPE_TONE_ADD:
|
||||
audio_scene_env.tone.decoder = audio_decoder_add(evt->p.tone_add.type, decoder_request_raw_data_cb);
|
||||
|
||||
if (audio_scene_env.tone.decoder) {
|
||||
audio_scene_env.tone.req_dec_cb = evt->p.tone_add.req_dec_cb;
|
||||
audio_decoder_start(audio_scene_env.tone.decoder);
|
||||
}
|
||||
break;
|
||||
case AUDIO_SCENE_EVT_TYPE_TONE_REMOVE:
|
||||
if (audio_scene_env.tone.decoder == evt->p.decoder) {
|
||||
audio_decoder_remove(audio_scene_env.tone.decoder);
|
||||
audio_scene_env.tone.decoder = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
while(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void sco_evt_handler(audio_scene_evt_t *evt)
|
||||
{
|
||||
audio_scene_t *scene = audio_scene_env.scene;
|
||||
sco_env_t *sco_env = &audio_scene_env.env.sco;
|
||||
|
||||
switch (evt->type) {
|
||||
case AUDIO_SCENE_EVT_TYPE_REQ_RAW_DATA:
|
||||
{
|
||||
uint32_t length = 0;
|
||||
if (evt->p.decoder == sco_env->decoder) {
|
||||
if (audio_decoder_decode(sco_env->decoder,
|
||||
NULL, &length) != AUDIO_RET_OUTPUT_ALMOTE_FULL) {
|
||||
if (sco_env->start_thd == 0) {
|
||||
audio_data_element_t *elt;
|
||||
int ret;
|
||||
elt = (void *)co_list_pick(&sco_env->sco_data_list);
|
||||
while (elt) {
|
||||
uint32_t length = elt->length - elt->offset;
|
||||
if (elt->valid) {
|
||||
uint32_t length = elt->length - elt->offset;
|
||||
ret = audio_decoder_decode(sco_env->decoder, &elt->buffer[elt->offset], &length);
|
||||
elt->offset += length;
|
||||
}
|
||||
else {
|
||||
uint32_t length = AUDIO_SPECIAL_LENGTH_FOR_PLC;
|
||||
ret = audio_decoder_decode(sco_env->decoder, NULL, &length);
|
||||
elt->offset += length;
|
||||
}
|
||||
if (elt->length == elt->offset) {
|
||||
elt = (void *)co_list_pop_front(&sco_env->sco_data_list);
|
||||
sco_env->sco_data_counter--;
|
||||
dram_free(elt);
|
||||
elt = (void *)co_list_pick(&sco_env->sco_data_list);
|
||||
}
|
||||
if (ret == AUDIO_RET_OUTPUT_ALMOTE_FULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (evt->p.decoder == audio_scene_env.tone.decoder) {
|
||||
if (audio_decoder_decode(audio_scene_env.tone.decoder,
|
||||
NULL, &length) != AUDIO_RET_OUTPUT_ALMOTE_FULL) {
|
||||
/* send request to app layer after checking PCM list */
|
||||
void (*req_dec_cb)(audio_decoder_t *) = audio_scene_env.tone.req_dec_cb;
|
||||
if (req_dec_cb) {
|
||||
req_dec_cb(audio_scene_env.tone.decoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AUDIO_SCENE_EVT_TYPE_RECV_RAW_DATA:
|
||||
co_list_push_back(&audio_scene_env.env.sco.sco_data_list, &evt->p.raw_data->hdr);
|
||||
sco_env->sco_data_counter++;
|
||||
if (sco_env->start_thd) {
|
||||
sco_env->start_thd--;
|
||||
}
|
||||
break;
|
||||
case AUDIO_SCENE_EVT_TYPE_TONE_ADD:
|
||||
audio_scene_env.tone.decoder = audio_decoder_add(evt->p.tone_add.type, decoder_request_raw_data_cb);
|
||||
|
||||
if (audio_scene_env.tone.decoder) {
|
||||
audio_scene_env.tone.req_dec_cb = evt->p.tone_add.req_dec_cb;
|
||||
audio_decoder_start(audio_scene_env.tone.decoder);
|
||||
}
|
||||
break;
|
||||
case AUDIO_SCENE_EVT_TYPE_TONE_REMOVE:
|
||||
if (audio_scene_env.tone.decoder == evt->p.decoder) {
|
||||
audio_decoder_remove(audio_scene_env.tone.decoder);
|
||||
audio_scene_env.tone.decoder = NULL;
|
||||
}
|
||||
break;
|
||||
case AUDIO_SCENE_EVT_TYPE_ADC_NEW_SAMPLES:
|
||||
{
|
||||
uint32_t sco_in_length;
|
||||
sco_env_t *sco = &audio_scene_env.env.sco;
|
||||
uint32_t left_space;
|
||||
uint32_t adc_samples = evt->p.adc_new_samples;
|
||||
uint32_t available_samples;
|
||||
|
||||
/* save adc data into framebuffer */
|
||||
left_space = sco->adc_input_size - sco->adc_input_wr_ptr;
|
||||
if (left_space > adc_samples) {
|
||||
audio_hw_read_pcm(sco->audio_hw_output, &sco->adc_input[sco->adc_input_wr_ptr], adc_samples, AUDIO_CHANNELS_MONO);
|
||||
sco->adc_input_wr_ptr += adc_samples;
|
||||
}
|
||||
else {
|
||||
audio_hw_read_pcm(sco->audio_hw_output, &sco->adc_input[sco->adc_input_wr_ptr], left_space, AUDIO_CHANNELS_MONO);
|
||||
adc_samples -= left_space;
|
||||
sco->adc_input_wr_ptr = 0;
|
||||
if (adc_samples) {
|
||||
audio_hw_read_pcm(sco->audio_hw_output, &sco->adc_input[0], adc_samples, AUDIO_CHANNELS_MONO);
|
||||
sco->adc_input_wr_ptr = adc_samples;
|
||||
}
|
||||
}
|
||||
|
||||
/* check whether received ADC data are enough for audio algorithm */
|
||||
if (sco->adc_input_wr_ptr >= sco->adc_input_rd_ptr) {
|
||||
available_samples = sco->adc_input_wr_ptr - sco->adc_input_rd_ptr;
|
||||
}
|
||||
else {
|
||||
available_samples = sco->adc_input_size + sco->adc_input_wr_ptr - sco->adc_input_rd_ptr;
|
||||
}
|
||||
|
||||
if (sco->decoder_output_wr_ptr < sco->algo_frame_size) {
|
||||
/* request decoded PCM data (from ESCO IN), and send data to audio algorithim */
|
||||
sco_in_length = audio_decoder_get_pcm(sco->decoder_to_algo,
|
||||
&sco->decoder_output[sco->decoder_output_wr_ptr],
|
||||
sco->algo_frame_size - sco->decoder_output_wr_ptr,
|
||||
AUDIO_CHANNELS_MONO);
|
||||
sco->decoder_output_wr_ptr += sco_in_length;
|
||||
}
|
||||
|
||||
if (available_samples >= sco->algo_frame_size) {
|
||||
// /* request decoded PCM data (from ESCO IN), and send data to audio algorithim */
|
||||
// sco_in_length = audio_decoder_get_pcm(sco->decoder_to_algo,
|
||||
// &sco->decoder_output[sco->decoder_output_wr_ptr],
|
||||
// sco->algo_frame_size - sco->decoder_output_wr_ptr,
|
||||
// AUDIO_CHANNELS_MONO);
|
||||
// sco->decoder_output_wr_ptr += sco_in_length;
|
||||
if (sco->decoder_output_wr_ptr >= sco->algo_frame_size) {
|
||||
int16_t *output = NULL;
|
||||
uint32_t encoded_frame;
|
||||
uint32_t aec_out_samples = sco->algo_frame_size;
|
||||
|
||||
sco->decoder_output_wr_ptr = 0;
|
||||
|
||||
/* request start algorithim */
|
||||
algorithm_launch(sco->audio_algo_handle,
|
||||
&sco->decoder_output[0],
|
||||
&sco->adc_input[sco->adc_input_rd_ptr],
|
||||
&output);
|
||||
sco->adc_input_rd_ptr += sco->algo_frame_size;
|
||||
if (sco->adc_input_rd_ptr >= sco->adc_input_size) {
|
||||
sco->adc_input_rd_ptr = 0;
|
||||
}
|
||||
|
||||
/* use AEC output to do encoder, send encoded SCO frame to remote device */
|
||||
audio_encoder_encode(sco->encoder,
|
||||
(const uint8_t *)output,
|
||||
sizeof(int16_t) * aec_out_samples,
|
||||
AUDIO_CHANNELS_MONO,
|
||||
audio_scene_env.scene->param.sco.sample_rate);
|
||||
|
||||
/* send encoded frame to peer device */
|
||||
encoded_frame = audio_encoder_get_frame_count(sco->encoder);
|
||||
while (encoded_frame--) {
|
||||
audio_encoder_frame_t *frame;
|
||||
frame = audio_encoder_frame_pop(sco->encoder);
|
||||
if (audio_scene_env.scene->param.sco.report_enc_cb) {
|
||||
audio_scene_env.scene->param.sco.report_enc_cb(audio_scene_env.scene->param.sco.report_enc_arg, frame->data, frame->length);
|
||||
}
|
||||
audio_encoder_frame_release(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
while(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _audio_scene_create(void)
|
||||
{
|
||||
audio_scene_t *audio_scene = audio_scene_env.scene;
|
||||
|
||||
if (audio_scene == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (audio_scene->type) {
|
||||
case AUDIO_SCENE_TYPE_A2DP_SINK:
|
||||
init_a2dp_sink();
|
||||
break;
|
||||
case AUDIO_SCENE_TYPE_SCO:
|
||||
init_sco();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _audio_scene_destroy(void)
|
||||
{
|
||||
audio_scene_t *scene = audio_scene_env.scene;
|
||||
|
||||
if (scene == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (scene->type) {
|
||||
case AUDIO_SCENE_TYPE_A2DP_SINK:
|
||||
destroy_a2dp_sink();
|
||||
break;
|
||||
case AUDIO_SCENE_TYPE_SCO:
|
||||
destroy_sco();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
vPortFree(audio_scene_env.scene);
|
||||
audio_scene_env.scene = NULL;
|
||||
}
|
||||
|
||||
static void audio_scene_task(void *arg)
|
||||
{
|
||||
audio_scene_evt_t *evt;
|
||||
audio_scene_t *scene;
|
||||
|
||||
while (1) {
|
||||
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
|
||||
GLOBAL_INT_DISABLE();
|
||||
evt = (void *)co_list_pop_front(&audio_scene_env.evt_list);
|
||||
GLOBAL_INT_RESTORE();
|
||||
|
||||
if (evt) {
|
||||
switch (evt->type) {
|
||||
case AUDIO_SCENE_EVT_TYPE_CREATE:
|
||||
if (audio_scene_env.scene == NULL) {
|
||||
audio_scene_env.scene = evt->p.scene;
|
||||
_audio_scene_create();
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
}
|
||||
break;
|
||||
case AUDIO_SCENE_EVT_TYPE_DESTROY:
|
||||
if (audio_scene_env.scene == evt->p.scene) {
|
||||
_audio_scene_destroy();
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
scene = audio_scene_env.scene;
|
||||
if (scene) {
|
||||
switch (scene->type) {
|
||||
case AUDIO_SCENE_TYPE_A2DP_SINK:
|
||||
a2dp_sink_evt_handler(evt);
|
||||
break;
|
||||
case AUDIO_SCENE_TYPE_SCO:
|
||||
sco_evt_handler(evt);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (evt->type == AUDIO_SCENE_EVT_TYPE_RECV_RAW_DATA) {
|
||||
dram_free(evt->p.raw_data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
vPortFree(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_scene_init(uint32_t stack_size, uint8_t priority)
|
||||
{
|
||||
co_list_init(&audio_scene_env.evt_list);
|
||||
audio_scene_env.scene = NULL;
|
||||
|
||||
xTaskCreate(audio_scene_task, "AUDIO_SCENE_TASK", stack_size, NULL, priority, &audio_scene_env.audio_task_handle);
|
||||
}
|
||||
|
||||
audio_scene_t *audio_scene_create(audio_scene_type_t type, void *param)
|
||||
{
|
||||
audio_scene_evt_t *evt;
|
||||
audio_scene_t *audio_scene;
|
||||
|
||||
evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
|
||||
|
||||
audio_scene = (void *)pvPortMalloc(sizeof(audio_scene_t));
|
||||
audio_scene->type = type;
|
||||
switch (type) {
|
||||
case AUDIO_SCENE_TYPE_A2DP_SINK:
|
||||
memcpy((void *)&audio_scene->param.a2dp_sink, param, sizeof(audio_scene_param_a2dp_sink_t));
|
||||
break;
|
||||
case AUDIO_SCENE_TYPE_SCO:
|
||||
memcpy((void *)&audio_scene->param.sco, param, sizeof(audio_scene_param_sco_t));
|
||||
break;
|
||||
default:
|
||||
goto __err;
|
||||
}
|
||||
|
||||
evt->type = AUDIO_SCENE_EVT_TYPE_CREATE;
|
||||
evt->p.scene = audio_scene;
|
||||
send_event(evt);
|
||||
|
||||
return audio_scene;
|
||||
|
||||
__err:
|
||||
vPortFree(audio_scene);
|
||||
vPortFree(evt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void audio_scene_destroy(audio_scene_t *scene)
|
||||
{
|
||||
audio_scene_evt_t *evt;
|
||||
evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
|
||||
evt->type = AUDIO_SCENE_EVT_TYPE_DESTROY;
|
||||
evt->p.scene = scene;
|
||||
send_event(evt);
|
||||
}
|
||||
|
||||
void audio_scene_recv_raw_data(audio_scene_t *scene, bool valid, uint8_t *buffer, uint32_t length)
|
||||
{
|
||||
audio_data_element_t *elt;
|
||||
|
||||
audio_scene_evt_t *evt;
|
||||
evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
|
||||
evt->type = AUDIO_SCENE_EVT_TYPE_RECV_RAW_DATA;
|
||||
if (valid) {
|
||||
elt = (void *)dram_malloc(sizeof(audio_data_element_t) + length);
|
||||
elt->valid = true;
|
||||
elt->length = length;
|
||||
elt->offset = 0;
|
||||
memcpy((void *)&elt->buffer[0], buffer, length);
|
||||
}
|
||||
else {
|
||||
elt = (void *)dram_malloc(sizeof(audio_data_element_t));
|
||||
elt->valid = false;
|
||||
elt->length = AUDIO_SPECIAL_LENGTH_FOR_PLC;
|
||||
elt->offset = 0;
|
||||
}
|
||||
evt->p.raw_data = elt;
|
||||
send_event(evt);
|
||||
}
|
||||
|
||||
bool audio_scene_dac_is_ready(audio_scene_t *scene)
|
||||
{
|
||||
return audio_decoder_is_started();
|
||||
}
|
203
MCU/components/modules/audio/audio_scene.h
Normal file
203
MCU/components/modules/audio/audio_scene.h
Normal file
@ -0,0 +1,203 @@
|
||||
#ifndef _AUDIO_SCENE_H
|
||||
#define _AUDIO_SCENE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#include "audio_common.h"
|
||||
#include "audio_hw.h"
|
||||
#include "audio_decoder.h"
|
||||
#include "audio_encoder.h"
|
||||
#include "voice_recognize.h"
|
||||
|
||||
typedef void (*audio_sence_report_encoded_frame)(void *arg, uint8_t *data, uint16_t length);
|
||||
|
||||
typedef enum {
|
||||
AUDIO_SCENE_TYPE_LOCAL_PLAYBACK,
|
||||
AUDIO_SCENE_TYPE_A2DP_SOURCE,
|
||||
AUDIO_SCENE_TYPE_A2DP_SINK,
|
||||
AUDIO_SCENE_TYPE_SCO,
|
||||
AUDIO_SCENE_TYPE_TONE,
|
||||
AUDIO_SCENE_TYPE_RECODER,
|
||||
AUDIO_SCENE_TYPE_VOICE_RECOGNIZE,
|
||||
} audio_scene_type_t;
|
||||
|
||||
typedef enum {
|
||||
AUDIO_SCENE_EVT_TYPE_CREATE,
|
||||
AUDIO_SCENE_EVT_TYPE_DESTROY,
|
||||
AUDIO_SCENE_EVT_TYPE_RECV_RAW_DATA,
|
||||
AUDIO_SCENE_EVT_TYPE_REQ_RAW_DATA,
|
||||
AUDIO_SCENE_EVT_TYPE_DECODER_ADD,
|
||||
AUDIO_SCENE_EVT_TYPE_DECODER_REMOVE,
|
||||
AUDIO_SCENE_EVT_TYPE_TONE_ADD,
|
||||
AUDIO_SCENE_EVT_TYPE_TONE_REMOVE,
|
||||
AUDIO_SCENE_EVT_TYPE_DO_ENCODE,
|
||||
AUDIO_SCENE_EVT_TYPE_ADC_NEW_SAMPLES,
|
||||
} audio_scene_evt_type_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t sample_rate;
|
||||
uint8_t channels;
|
||||
audio_hw_type_t hw_type;
|
||||
uint32_t hw_base_addr;
|
||||
void (*req_dec_cb)(audio_decoder_t *);
|
||||
} audio_scene_param_local_playback_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t sample_rate;
|
||||
uint8_t channels;
|
||||
audio_type_t audio_type;
|
||||
audio_hw_type_t hw_type;
|
||||
uint32_t hw_base_addr;
|
||||
void (*req_dec_cb)(audio_decoder_t *);
|
||||
} audio_scene_param_a2dp_sink_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t sample_rate;
|
||||
uint8_t channels;
|
||||
audio_type_t audio_type;
|
||||
void (*req_dec_cb)(audio_decoder_t *);
|
||||
} audio_scene_param_a2dp_source_t;
|
||||
|
||||
typedef struct {
|
||||
audio_type_t audio_type;
|
||||
audio_hw_type_t hw_type;
|
||||
uint32_t sample_rate;
|
||||
uint32_t hw_base_addr;
|
||||
void (*req_dec_cb)(audio_decoder_t *);
|
||||
audio_sence_report_encoded_frame report_enc_cb;
|
||||
void *report_enc_arg;
|
||||
} audio_scene_param_sco_t;
|
||||
|
||||
typedef struct {
|
||||
audio_type_t audio_type;
|
||||
audio_hw_type_t hw_type;
|
||||
uint32_t hw_base_addr;
|
||||
void (*req_dec_cb)(audio_decoder_t *);
|
||||
} audio_scene_param_tone_t;
|
||||
|
||||
typedef struct {
|
||||
audio_scene_type_t type;
|
||||
|
||||
union {
|
||||
audio_scene_param_local_playback_t local_playback;
|
||||
audio_scene_param_a2dp_sink_t a2dp_sink;
|
||||
audio_scene_param_a2dp_source_t a2dp_source;
|
||||
audio_scene_param_sco_t sco;
|
||||
audio_scene_param_voice_recognize_t voice_recognize;
|
||||
} param;
|
||||
|
||||
audio_scene_param_tone_t tone;
|
||||
} audio_scene_t;
|
||||
|
||||
typedef struct {
|
||||
struct co_list_hdr hdr;
|
||||
|
||||
audio_scene_evt_type_t type;
|
||||
union {
|
||||
/* used to create, delete scene */
|
||||
audio_scene_t *scene;
|
||||
/* used to append raw data */
|
||||
audio_data_element_t *raw_data;
|
||||
/* used for remove a decoder or request new data */
|
||||
audio_decoder_t *decoder;
|
||||
/* used for encoder */
|
||||
uint32_t encode_samples;
|
||||
/* used for tone decoder */
|
||||
struct {
|
||||
audio_type_t type;
|
||||
void (*req_dec_cb)(audio_decoder_t *);
|
||||
} tone_add;
|
||||
/* used for sco */
|
||||
uint32_t adc_new_samples;
|
||||
} p;
|
||||
} audio_scene_evt_t;
|
||||
|
||||
typedef struct {
|
||||
audio_decoder_t *decoder;
|
||||
audio_decoder_output_t *decoder_to_hw;
|
||||
audio_hw_t *audio_hw;
|
||||
} local_playback_env_t;
|
||||
|
||||
typedef struct {
|
||||
audio_decoder_t *decoder;
|
||||
audio_decoder_output_t *decoder_to_hw;
|
||||
audio_hw_t *audio_hw;
|
||||
|
||||
struct co_list a2dp_data_list;
|
||||
uint32_t a2dp_data_counter;
|
||||
uint8_t start_thd;
|
||||
} a2dp_sink_env_t;
|
||||
|
||||
typedef struct {
|
||||
audio_decoder_t *decoder;
|
||||
audio_decoder_output_t *decoder_to_encoder;
|
||||
audio_encoder_t *encoder;
|
||||
|
||||
uint8_t *buffer;
|
||||
uint32_t offset; // unit is sample
|
||||
} a2dp_source_env_t;
|
||||
|
||||
typedef struct {
|
||||
audio_decoder_t *decoder; // for sco data decoder, PLC is included
|
||||
audio_decoder_output_t *decoder_to_algo; // decoded data routed to algorithm
|
||||
audio_decoder_output_t *decoder_to_hw; // decoded data routed to DAC
|
||||
audio_encoder_t *encoder; // for sco data encoder, input data is output of AEC
|
||||
audio_hw_t *audio_hw;
|
||||
audio_hw_output_t *audio_hw_output; // used to receive ADC data
|
||||
void *audio_algo_handle;
|
||||
|
||||
struct co_list sco_data_list;
|
||||
uint16_t sco_data_counter;
|
||||
uint8_t start_thd;
|
||||
|
||||
uint32_t algo_frame_size; // unit is sample
|
||||
|
||||
int16_t *decoder_output; // next step is algorithm, size is algo_frame_size
|
||||
uint32_t decoder_output_wr_ptr; // unit is sample
|
||||
|
||||
int16_t *adc_input; // next step is algorithm, size is adc_input_size = N * algo_frame_size
|
||||
uint32_t adc_input_size; // unit is sample
|
||||
uint32_t adc_input_wr_ptr; // unit is sample
|
||||
uint32_t adc_input_rd_ptr; // unit is sample
|
||||
} sco_env_t;
|
||||
|
||||
typedef struct {
|
||||
audio_decoder_t *decoder;
|
||||
audio_decoder_output_t *decoder_to_hw;
|
||||
audio_hw_t *audio_hw;
|
||||
|
||||
void (*req_dec_cb)(audio_decoder_t *);
|
||||
} tone_env_t;
|
||||
|
||||
typedef struct {
|
||||
audio_scene_t *scene;
|
||||
|
||||
union {
|
||||
local_playback_env_t local_playback;
|
||||
a2dp_sink_env_t a2dp_sink;
|
||||
a2dp_source_env_t a2dp_source;
|
||||
sco_env_t sco;
|
||||
voice_recognize_env_t voice_recognize;
|
||||
} env;
|
||||
|
||||
tone_env_t tone;
|
||||
|
||||
TaskHandle_t audio_task_handle;
|
||||
struct co_list evt_list;
|
||||
} audio_scene_env_t;
|
||||
|
||||
void audio_scene_init(uint32_t stack_size, uint8_t priority);
|
||||
|
||||
audio_scene_t *audio_scene_create(audio_scene_type_t type, void *param);
|
||||
void audio_scene_destroy(audio_scene_t *scene);
|
||||
|
||||
/* used for a2dp_sink, sco when received new data frome remote side */
|
||||
void audio_scene_recv_raw_data(audio_scene_t *scene, bool valid, uint8_t *buffer, uint32_t length);
|
||||
|
||||
/* used to check wheter PA can be enabled, eliminate POP noise */
|
||||
bool audio_scene_dac_is_ready(audio_scene_t *scene);
|
||||
|
||||
#endif // _AUDIO_SCENE_H
|
330
MCU/components/modules/audio/audio_test.c
Normal file
330
MCU/components/modules/audio/audio_test.c
Normal file
@ -0,0 +1,330 @@
|
||||
#include "audio_test.h"
|
||||
#include "audio_scene.h"
|
||||
|
||||
#include "fr30xx.h"
|
||||
|
||||
#include "mp3_sample.h"
|
||||
#include "sbc_sample.h"
|
||||
#include "msbc_sample.h"
|
||||
|
||||
static audio_scene_t *local_playback_scene;
|
||||
static audio_scene_t *a2dp_sink_scene;
|
||||
static audio_scene_t *a2dp_source_scene;
|
||||
static audio_scene_t *sco_source_scene;
|
||||
|
||||
static uint32_t a2dp_sink_sbc_index = 0;
|
||||
static bool msbc_ready = false;
|
||||
|
||||
static void local_playback_request_raw_data(audio_decoder_t *decoder)
|
||||
{
|
||||
#define RAW_FRAME_SIZE 512
|
||||
static uint32_t index = 0;
|
||||
uint32_t length;
|
||||
uint32_t total_length = mp3_sample_get_size();
|
||||
audio_ret_t ret;
|
||||
|
||||
{
|
||||
fputc('{', &__stdout);
|
||||
do {
|
||||
length = RAW_FRAME_SIZE;
|
||||
if ((length + index) > total_length) {
|
||||
length = total_length - index;
|
||||
}
|
||||
|
||||
ret = audio_scene_decode(decoder, &mp3_sample[index], &length);
|
||||
index += length;
|
||||
if (index >= total_length) {
|
||||
index = 0;
|
||||
break;
|
||||
}
|
||||
} while (ret != AUDIO_RET_OUTPUT_ALMOTE_FULL);
|
||||
fputc('}', &__stdout);
|
||||
}
|
||||
}
|
||||
|
||||
static void a2dp_sink_request_raw_data(audio_decoder_t *decoder)
|
||||
{
|
||||
#define RAW_FRAME_SIZE 512
|
||||
|
||||
uint32_t length;
|
||||
uint32_t total_length = sbc_sample_get_size();
|
||||
audio_ret_t ret;
|
||||
|
||||
{
|
||||
fputc('{', &__stdout);
|
||||
do {
|
||||
length = RAW_FRAME_SIZE;
|
||||
if ((length + a2dp_sink_sbc_index) > total_length) {
|
||||
length = total_length - a2dp_sink_sbc_index;
|
||||
}
|
||||
|
||||
ret = audio_scene_decode(decoder, &sbc_sample[a2dp_sink_sbc_index], &length);
|
||||
a2dp_sink_sbc_index += length;
|
||||
if (a2dp_sink_sbc_index >= total_length) {
|
||||
a2dp_sink_sbc_index = 0;
|
||||
break;
|
||||
}
|
||||
} while (ret != AUDIO_RET_OUTPUT_ALMOTE_FULL);
|
||||
fputc('}', &__stdout);
|
||||
}
|
||||
}
|
||||
|
||||
static void a2dp_source_request_raw_data(audio_decoder_t *decoder)
|
||||
{
|
||||
#define RAW_FRAME_SIZE 512
|
||||
static uint32_t index = 0;
|
||||
uint32_t length;
|
||||
uint32_t total_length = mp3_sample_get_size();
|
||||
audio_ret_t ret;
|
||||
|
||||
{
|
||||
fputc('{', &__stdout);
|
||||
do {
|
||||
length = RAW_FRAME_SIZE;
|
||||
if ((length + index) > total_length) {
|
||||
length = total_length - index;
|
||||
}
|
||||
|
||||
ret = audio_scene_decode(decoder, &mp3_sample[index], &length);
|
||||
index += length;
|
||||
if (index >= total_length) {
|
||||
index = 0;
|
||||
break;
|
||||
}
|
||||
} while (ret != AUDIO_RET_OUTPUT_ALMOTE_FULL);
|
||||
fputc('}', &__stdout);
|
||||
}
|
||||
}
|
||||
|
||||
static void sco_request_raw_data(audio_decoder_t *decoder)
|
||||
{
|
||||
#ifdef RAW_FRAME_SIZE
|
||||
#undef RAW_FRAME_SIZE
|
||||
#endif
|
||||
#define RAW_FRAME_SIZE 57
|
||||
static uint32_t index = 0;
|
||||
uint32_t length;
|
||||
uint32_t total_length = msbc_sample_get_size();
|
||||
audio_ret_t ret;
|
||||
|
||||
if (msbc_ready == false) {
|
||||
return;
|
||||
}
|
||||
msbc_ready = false;
|
||||
|
||||
{
|
||||
fputc('{', &__stdout);
|
||||
do {
|
||||
length = RAW_FRAME_SIZE;
|
||||
if ((length + index) > total_length) {
|
||||
length = total_length - index;
|
||||
}
|
||||
|
||||
ret = audio_scene_decode(decoder, &msbc_sample[index], &length);
|
||||
index += length;
|
||||
if (index >= total_length) {
|
||||
index = 0;
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
fputc('}', &__stdout);
|
||||
}
|
||||
}
|
||||
|
||||
static void tone_request_raw_data(audio_decoder_t *decoder)
|
||||
{
|
||||
#ifdef RAW_FRAME_SIZE
|
||||
#undef RAW_FRAME_SIZE
|
||||
#endif
|
||||
#define RAW_FRAME_SIZE 512
|
||||
static uint32_t index = 0;
|
||||
uint32_t length;
|
||||
uint32_t total_length = mp3_sample_get_size();
|
||||
audio_ret_t ret;
|
||||
|
||||
{
|
||||
fputc('[', &__stdout);
|
||||
do {
|
||||
length = RAW_FRAME_SIZE;
|
||||
if ((length + index) > total_length) {
|
||||
length = total_length - index;
|
||||
}
|
||||
|
||||
ret = audio_scene_decode(decoder, &mp3_sample[index], &length);
|
||||
index += length;
|
||||
if (index >= total_length) {
|
||||
index = 0;
|
||||
audio_scene_tone_stop();
|
||||
break;
|
||||
}
|
||||
} while (ret != AUDIO_RET_OUTPUT_ALMOTE_FULL);
|
||||
fputc(']', &__stdout);
|
||||
}
|
||||
}
|
||||
|
||||
static void local_playback_start(void)
|
||||
{
|
||||
audio_scene_param_local_playback_t param;
|
||||
|
||||
param.sample_rate = 44100;
|
||||
param.channels = 2;
|
||||
param.hw_type = AUDIO_HW_TYPE_CODEC;
|
||||
param.hw_base_addr = I2S0_BASE;
|
||||
param.req_dec_cb = local_playback_request_raw_data;
|
||||
local_playback_scene = audio_scene_create(AUDIO_SCENE_TYPE_LOCAL_PLAYBACK, ¶m, 1024, configMAX_PRIORITIES-1);
|
||||
}
|
||||
|
||||
static void local_playback_stop(void)
|
||||
{
|
||||
audio_scene_destroy(local_playback_scene);
|
||||
}
|
||||
|
||||
static void a2dp_sink_start(void)
|
||||
{
|
||||
audio_scene_param_a2dp_sink_t param;
|
||||
|
||||
param.sample_rate = 44100;
|
||||
param.channels = 2;
|
||||
param.audio_type = AUDIO_TYPE_SBC;
|
||||
param.hw_type = AUDIO_HW_TYPE_I2S;
|
||||
param.hw_base_addr = I2S0_BASE;
|
||||
param.req_dec_cb = a2dp_sink_request_raw_data;
|
||||
a2dp_sink_scene = audio_scene_create(AUDIO_SCENE_TYPE_A2DP_SINK, ¶m, 1024, configMAX_PRIORITIES-1);
|
||||
}
|
||||
|
||||
static void a2dp_sink_stop(void)
|
||||
{
|
||||
audio_scene_destroy(a2dp_sink_scene);
|
||||
}
|
||||
|
||||
static void a2dp_source_start(void)
|
||||
{
|
||||
audio_scene_param_a2dp_source_t param;
|
||||
|
||||
param.sample_rate = 44100;
|
||||
param.channels = 2;
|
||||
param.req_dec_cb = a2dp_source_request_raw_data;
|
||||
param.audio_type = AUDIO_TYPE_SBC;
|
||||
a2dp_source_scene = audio_scene_create(AUDIO_SCENE_TYPE_A2DP_SOURCE, ¶m, 1024, configMAX_PRIORITIES-1);
|
||||
}
|
||||
|
||||
static void a2dp_source_stop(void)
|
||||
{
|
||||
audio_scene_destroy(a2dp_source_scene);
|
||||
}
|
||||
|
||||
static void a2dp_source_encode(void)
|
||||
{
|
||||
audio_scene_encode_reqeust(40);
|
||||
}
|
||||
|
||||
static void sco_start(void)
|
||||
{
|
||||
audio_scene_param_sco_t param;
|
||||
|
||||
if (sco_source_scene) {
|
||||
return;
|
||||
}
|
||||
|
||||
param.audio_type = AUDIO_TYPE_MSBC;
|
||||
param.hw_type = AUDIO_HW_TYPE_I2S;
|
||||
param.hw_base_addr = I2S0_BASE;
|
||||
param.req_dec_cb = sco_request_raw_data;
|
||||
param.report_enc_cb = NULL;
|
||||
param.sample_rate = 16000;
|
||||
|
||||
sco_source_scene = audio_scene_create(AUDIO_SCENE_TYPE_SCO, ¶m, 1024, configMAX_PRIORITIES-1);
|
||||
|
||||
timer_init(Timer0, (system_get_CoreClock() / 1000) * 30); // 7.5ms * 4
|
||||
timer_start(Timer0);
|
||||
NVIC_EnableIRQ(TIMER0_IRQn);
|
||||
}
|
||||
|
||||
static void sco_stop(void)
|
||||
{
|
||||
NVIC_DisableIRQ(TIMER0_IRQn);
|
||||
audio_scene_destroy(sco_source_scene);
|
||||
sco_source_scene = NULL;
|
||||
}
|
||||
|
||||
static void tone_start(void)
|
||||
{
|
||||
audio_scene_param_tone_t param;
|
||||
|
||||
param.audio_type = AUDIO_TYPE_MP3;
|
||||
param.hw_type = AUDIO_HW_TYPE_I2S;
|
||||
param.hw_base_addr = I2S0_BASE;
|
||||
param.req_dec_cb = tone_request_raw_data;
|
||||
|
||||
audio_scene_tone_play(¶m, 1024, configMAX_PRIORITIES-1);
|
||||
}
|
||||
|
||||
static void sco_income_data(void)
|
||||
{
|
||||
msbc_ready = true;
|
||||
// audio_decoder_t *decoder;
|
||||
// uint32_t length;
|
||||
// static uint32_t msbc_index = 0;
|
||||
//
|
||||
// decoder = audio_scene_decoder_get(AUDIO_SCENE_TYPE_SCO);
|
||||
// if (decoder) {
|
||||
// length = 57;
|
||||
// audio_scene_decode(decoder, &msbc_sample[msbc_index], &length);
|
||||
// msbc_index += 57;
|
||||
// if (msbc_index >= msbc_sample_get_size()) {
|
||||
// msbc_index = 0;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
static void tone_stop(void)
|
||||
{
|
||||
audio_scene_tone_stop();
|
||||
}
|
||||
|
||||
void audio_test(uint8_t sub_cmd, uint8_t *param)
|
||||
{
|
||||
switch(sub_cmd) {
|
||||
case 'A':
|
||||
local_playback_start();
|
||||
break;
|
||||
case 'B':
|
||||
local_playback_stop();
|
||||
break;
|
||||
case 'C':
|
||||
tone_start();
|
||||
break;
|
||||
case 'D':
|
||||
tone_stop();
|
||||
break;
|
||||
case 'E':
|
||||
a2dp_source_start();
|
||||
break;
|
||||
case 'F':
|
||||
a2dp_source_stop();
|
||||
break;
|
||||
case 'G':
|
||||
a2dp_source_encode();
|
||||
break;
|
||||
case 'H':
|
||||
a2dp_sink_sbc_index = 0;
|
||||
a2dp_sink_start();
|
||||
break;
|
||||
case 'I':
|
||||
a2dp_sink_stop();
|
||||
break;
|
||||
case 'J':
|
||||
sco_start();
|
||||
break;
|
||||
case 'K':
|
||||
sco_stop();
|
||||
break;
|
||||
case 'L':
|
||||
sco_income_data();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
printf("OK\r\n");
|
||||
}
|
8
MCU/components/modules/audio/audio_test.h
Normal file
8
MCU/components/modules/audio/audio_test.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef _AUDIO_TEST_H
|
||||
#define _AUDIO_TEST_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void audio_test(uint8_t sub_cmd, uint8_t *param);
|
||||
|
||||
#endif // _AUDIO_TEST_H
|
319
MCU/components/modules/audio/codec/codec.c
Normal file
319
MCU/components/modules/audio/codec/codec.c
Normal file
@ -0,0 +1,319 @@
|
||||
/*
|
||||
* codec.c
|
||||
*
|
||||
* Created on: 2018-3-28
|
||||
* Author: Administrator
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "codec.h"
|
||||
#include "audio_rpmsg.h"
|
||||
#include "dsp.h"
|
||||
|
||||
/************************************************************************************
|
||||
* @fn codec_decoder_init
|
||||
*
|
||||
* @brief reqeust to init a decoder instance.
|
||||
*
|
||||
* @param decoder_type: decoder type, @ref codec_decoder_type.
|
||||
* @param param: decoder parameters.
|
||||
*
|
||||
* @return decoder instance, the value should be NULL when initialization is failed.
|
||||
*/
|
||||
struct codec_decoder_handle *codec_decoder_init(uint8_t decoder_type, void *param)
|
||||
{
|
||||
struct rpmsg_sync_msg_decoder_init_t *sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_decoder_init_t));
|
||||
if (sync_msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
sync_msg->decoder_type = decoder_type;
|
||||
sync_msg->param = param;
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_DEC_INIT, sync_msg, (uint32_t *)&result);
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/************************************************************************************
|
||||
* @fn codec_decoder_destroy
|
||||
*
|
||||
* @brief remove a created decoder instance.
|
||||
*
|
||||
* @param handle: decoder instance
|
||||
*/
|
||||
void codec_decoder_destroy(struct codec_decoder_handle *handle)
|
||||
{
|
||||
struct rpmsg_sync_msg_decoder_destroy_t sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
|
||||
if (handle == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
sync_msg.handle = handle;
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_DEC_DESTROY, (void *)&sync_msg, (uint32_t *)&result);
|
||||
}
|
||||
|
||||
/************************************************************************************
|
||||
* @fn codec_decoder_decode
|
||||
*
|
||||
* @brief reqeust to execute decode.
|
||||
*
|
||||
* @param handle: decoder instance
|
||||
* @param in_buf: buffer used to store raw data, caller should take care that this
|
||||
* buffer should be accessable for dsp.
|
||||
* @param in_length: the length of raw data, this value will be updated to length of
|
||||
* dealed data after decode operation is executed.
|
||||
* @param out_buf: used to store the buffer address of decoded data. Decoder will allocate
|
||||
* out buffer for internal used to store decoded data. If *out_buf is NULL,
|
||||
* decoder will update *out_buf to internal used out buffer address. If *out_buf
|
||||
* is not NULL, the decoded data will be copied into *out_buf.
|
||||
* @param out_length: the length of decoded data.
|
||||
*
|
||||
* @return execute result.
|
||||
*/
|
||||
int codec_decoder_decode(struct codec_decoder_handle *handle,
|
||||
const uint8_t *in_buf,
|
||||
uint32_t *in_length,
|
||||
uint8_t **out_buf,
|
||||
uint32_t *out_length)
|
||||
{
|
||||
struct rpmsg_sync_msg_decoder_exec_t *sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
bool trans_addr = false;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_decoder_exec_t));
|
||||
if (sync_msg == NULL) {
|
||||
return CODEC_ERROR_INSUFFICIENT_RESOURCE;
|
||||
}
|
||||
|
||||
if (((uint32_t)in_buf >= DSP_DRAM_MCU_BASE_ADDR) && ((uint32_t)in_buf < (DSP_DRAM_MCU_BASE_ADDR+DSP_DRAM_SIZE))) {
|
||||
in_buf = (const void *)MCU_SRAM_2_DSP_DRAM(in_buf);
|
||||
}
|
||||
|
||||
sync_msg->handle = handle;
|
||||
sync_msg->in_buffer = in_buf;
|
||||
sync_msg->in_length = in_length;
|
||||
sync_msg->out_buffer = out_buf;
|
||||
sync_msg->out_length = out_length;
|
||||
|
||||
if (*out_buf == NULL) {
|
||||
trans_addr = true;
|
||||
}
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_DEC_EXEC, sync_msg, (uint32_t *)&result);
|
||||
|
||||
if (trans_addr) {
|
||||
if ((uint32_t)*out_buf >= DSP_DRAM_BASE_ADDR) {
|
||||
*out_buf = (void *)DSP_DRAM_2_MCU_SRAM(*out_buf);
|
||||
}
|
||||
}
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
/************************************************************************************
|
||||
* @fn codec_decoder_plc
|
||||
*
|
||||
* @brief reqeust to execute packet loss compensation.
|
||||
*
|
||||
* @param handle: decoder instance
|
||||
* @param out_buf: used to store the buffer address of decoded data. Decoder will allocate
|
||||
* out buffer for internal used to store decoded data. If *out_buf is NULL,
|
||||
* decoder will update *out_buf to internal used out buffer address. If *out_buf
|
||||
* is not NULL, the decoded data will be copied into *out_buf.
|
||||
* @param out_length: the length of decoded data.
|
||||
*
|
||||
* @return execute result.
|
||||
*/
|
||||
int codec_decoder_plc(struct codec_decoder_handle *handle,
|
||||
uint8_t **out_buf,
|
||||
uint32_t *out_length)
|
||||
{
|
||||
struct rpmsg_sync_msg_decoder_plc_t *sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
bool trans_addr = false;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_decoder_exec_t));
|
||||
if (sync_msg == NULL) {
|
||||
return CODEC_ERROR_INSUFFICIENT_RESOURCE;
|
||||
}
|
||||
sync_msg->handle = handle;
|
||||
sync_msg->out_buffer = out_buf;
|
||||
sync_msg->out_length = out_length;
|
||||
|
||||
if (*out_buf == NULL) {
|
||||
trans_addr = true;
|
||||
}
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_DEC_PLC, sync_msg, (uint32_t *)&result);
|
||||
|
||||
if (trans_addr) {
|
||||
if ((uint32_t)*out_buf >= DSP_DRAM_BASE_ADDR) {
|
||||
*out_buf = (void *)DSP_DRAM_2_MCU_SRAM(*out_buf);
|
||||
}
|
||||
}
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
/************************************************************************************
|
||||
* @fn codec_decoder_get_param
|
||||
*
|
||||
* @brief get information of latest decoded frame.
|
||||
*
|
||||
* @param handle: decoder instance
|
||||
* @param sample_rate: sample rate.
|
||||
* @param channels: channel number.
|
||||
*
|
||||
* @return 0: get success, 1: get failed.
|
||||
*/
|
||||
int codec_decoder_get_param(struct codec_decoder_handle *handle, uint32_t *sample_rate, uint8_t *channels)
|
||||
{
|
||||
struct rpmsg_sync_msg_decoder_get_param_t *sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_decoder_get_param_t));
|
||||
if (sync_msg == NULL) {
|
||||
return CODEC_ERROR_INSUFFICIENT_RESOURCE;
|
||||
}
|
||||
sync_msg->handle = handle;
|
||||
sync_msg->sample_rate = sample_rate;
|
||||
sync_msg->channels = channels;
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_DEC_GET_PARAM, sync_msg, (uint32_t *)&result);
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
/************************************************************************************
|
||||
* @fn codec_encoder_init
|
||||
*
|
||||
* @brief reqeust to init a encoder instance.
|
||||
*
|
||||
* @param encoder_type: encoder type, @ref codec_encoder_type.
|
||||
* @param param: encoder parameters.
|
||||
*
|
||||
* @return encoder instance, the value should be NULL when initialization is failed.
|
||||
*/
|
||||
struct codec_encoder_handle *codec_encoder_init(uint8_t encoder_type, void *param)
|
||||
{
|
||||
struct rpmsg_sync_msg_encoder_init_t *sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_encoder_init_t));
|
||||
if (sync_msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
sync_msg->encoder_type = encoder_type;
|
||||
sync_msg->param = param;
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_ENC_INIT, sync_msg, (uint32_t *)&result);
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/************************************************************************************
|
||||
* @fn codec_encoder_destroy
|
||||
*
|
||||
* @brief remove a created encoder instance.
|
||||
*
|
||||
* @param handle: encoder instance
|
||||
*/
|
||||
void codec_encoder_destroy(struct codec_encoder_handle *handle)
|
||||
{
|
||||
struct rpmsg_sync_msg_encoder_destroy_t sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
|
||||
if (handle == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
sync_msg.handle = handle;
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_ENC_DESTROY, (void *)&sync_msg, (uint32_t *)&result);
|
||||
}
|
||||
|
||||
/************************************************************************************
|
||||
* @fn codec_encoder_encode
|
||||
*
|
||||
* @brief reqeust to execute encode.
|
||||
*
|
||||
* @param handle: encoder instance
|
||||
* @param in_buf: buffer used to store PCM data, caller should take care that this
|
||||
* buffer should be accessable for dsp.
|
||||
* @param in_length: the length of PCM data, this value will be updated to length of
|
||||
* dealed data after encode operation is executed.
|
||||
* @param out_buf: used to store the buffer address of encoded data. Encoder will allocate
|
||||
* out buffer for internal used to store decoded data. If *out_buf is NULL,
|
||||
* decoder will update *out_buf to internal used out buffer address. If *out_buf
|
||||
* is not NULL, the decoded data will be copied into *out_buf.
|
||||
* @param out_length: the length of encoded data.
|
||||
*
|
||||
* @return execute result.
|
||||
*/
|
||||
int codec_encoder_encode(struct codec_encoder_handle *handle,
|
||||
const uint8_t *in_buf,
|
||||
uint32_t *in_length,
|
||||
uint8_t **out_buf,
|
||||
uint32_t *out_length)
|
||||
{
|
||||
struct rpmsg_sync_msg_encoder_exec_t *sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
bool trans_addr = false;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_encoder_exec_t));
|
||||
if (sync_msg == NULL) {
|
||||
return CODEC_ERROR_INSUFFICIENT_RESOURCE;
|
||||
}
|
||||
|
||||
if (((uint32_t)in_buf >= DSP_DRAM_MCU_BASE_ADDR) && ((uint32_t)in_buf < (DSP_DRAM_MCU_BASE_ADDR+DSP_DRAM_SIZE))) {
|
||||
in_buf = (void *)MCU_SRAM_2_DSP_DRAM(in_buf);
|
||||
}
|
||||
|
||||
sync_msg->handle = handle;
|
||||
sync_msg->in_buffer = in_buf;
|
||||
sync_msg->in_length = in_length;
|
||||
sync_msg->out_buffer = out_buf;
|
||||
sync_msg->out_length = out_length;
|
||||
|
||||
if (*out_buf == NULL) {
|
||||
trans_addr = true;
|
||||
}
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_ENC_EXEC, sync_msg, (uint32_t *)&result);
|
||||
|
||||
if (trans_addr) {
|
||||
if ((uint32_t)*out_buf >= DSP_DRAM_BASE_ADDR) {
|
||||
*out_buf = (void *)DSP_DRAM_2_MCU_SRAM(*out_buf);
|
||||
}
|
||||
}
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
204
MCU/components/modules/audio/codec/codec.h
Normal file
204
MCU/components/modules/audio/codec/codec.h
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* codec.h
|
||||
*
|
||||
* Created on: 2018-3-28
|
||||
* Author: Administrator
|
||||
*/
|
||||
|
||||
#ifndef _CODEC_H
|
||||
#define _CODEC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "FreeRTOS.h" // for malloc
|
||||
|
||||
#define codec_alloc pvPortMalloc
|
||||
#define codec_free vPortFree
|
||||
|
||||
enum codec_error {
|
||||
CODEC_ERROR_NO_ERROR,
|
||||
CODEC_ERROR_FAILED,
|
||||
CODEC_ERROR_INVALID_HANDLE,
|
||||
CODEC_ERROR_UNACCEPTABLE_PARAM,
|
||||
CODEC_ERROR_INSUFFICIENT_RESOURCE,
|
||||
CODEC_ERROR_NONE_FATAL,
|
||||
CODEC_ERROR_FATAL,
|
||||
CODEC_ERROR_NEED_MORE_DATA,
|
||||
};
|
||||
|
||||
enum codec_decoder_type {
|
||||
CODEC_DECODER_TYPE_MP3,
|
||||
CODEC_DECODER_TYPE_CVSD,
|
||||
CODEC_DECODER_TYPE_LC3,
|
||||
CODEC_DECODER_TYPE_MSBC,
|
||||
CODEC_DECODER_TYPE_SBC,
|
||||
CODEC_DECODER_TYPE_OGGOPUS,
|
||||
CODEC_DECODER_TYPE_AAC,
|
||||
CODEC_DECODER_TYPE_PCM,
|
||||
};
|
||||
|
||||
enum codec_encoder_type {
|
||||
CODEC_ENCODER_TYPE_CVSD,
|
||||
CODEC_ENCODER_TYPE_LC3,
|
||||
CODEC_ENCODER_TYPE_MSBC,
|
||||
CODEC_ENCODER_TYPE_SBC,
|
||||
CODEC_ENCODER_TYPE_OPUS,
|
||||
CODEC_ENCODER_TYPE_AAC,
|
||||
};
|
||||
|
||||
struct sbc_encoder_param {
|
||||
uint32_t i_samp_freq;
|
||||
uint32_t i_num_chan;
|
||||
uint32_t i_subbands;
|
||||
uint32_t i_blocks;
|
||||
uint32_t i_bitpool;
|
||||
};
|
||||
|
||||
struct aac_encoder_param {
|
||||
uint32_t i_samp_freq;
|
||||
uint32_t i_num_chan;
|
||||
uint32_t i_pcm_wdsz;
|
||||
};
|
||||
|
||||
struct sbc_orign_encoder_param{
|
||||
int subbands;
|
||||
int bitpool;
|
||||
int joint;
|
||||
int dualchannel;
|
||||
int snr;
|
||||
int blocks;
|
||||
bool msbc;
|
||||
uint32_t input_size;
|
||||
|
||||
uint32_t channels;
|
||||
uint32_t sample_rate; /* sample rate */
|
||||
// uint8_t sbc_mode;
|
||||
};
|
||||
|
||||
struct msbc_encoder_param {
|
||||
uint32_t i_bitrate;
|
||||
uint32_t i_samp_freq;
|
||||
};
|
||||
|
||||
struct lc3_encoder_param{
|
||||
int dt_us;
|
||||
int sr_hz;
|
||||
int sr_pcm_hz;
|
||||
int pcm_format;
|
||||
};
|
||||
|
||||
struct lc3_hifi3z_encoder_param{
|
||||
int dt_ms;
|
||||
int sr_hz;
|
||||
int bit_rate;
|
||||
int bips_in;
|
||||
int ch;
|
||||
};
|
||||
|
||||
struct lc3_hifi3z_decoder_param{
|
||||
int bips_out;
|
||||
uint32_t sample_rate;
|
||||
int16_t nchannels;
|
||||
int bitrate;
|
||||
float frame_ms;
|
||||
uint32_t signal_len;
|
||||
int epmode;
|
||||
int hrmode;
|
||||
uint16_t frame_size;
|
||||
};
|
||||
|
||||
struct cvsd_encoder_param{
|
||||
int ch;
|
||||
double step_decay; //I2 decay
|
||||
double accum_decay; //I1 decay
|
||||
};
|
||||
|
||||
struct cvsd_decoder_param{
|
||||
int ch;
|
||||
|
||||
double step_decay; //I2 decay
|
||||
double accum_decay; //I1 decay
|
||||
};
|
||||
|
||||
struct lc3_decoder_param{
|
||||
int dt_us;
|
||||
int sr_hz;
|
||||
int sr_pcm_hz;
|
||||
int pcm_format;
|
||||
};
|
||||
|
||||
struct opus_decoder_param{
|
||||
int sample_rate;
|
||||
int nb_coupled;
|
||||
int channel_mapping;
|
||||
uint8_t channels;
|
||||
uint8_t frames_per_pack;
|
||||
int gain;
|
||||
};
|
||||
|
||||
struct opus_encoder_param{
|
||||
int application;
|
||||
int sampleRate;
|
||||
int numChannels;
|
||||
int bitRate;
|
||||
float frame_size_ms;
|
||||
};
|
||||
|
||||
struct aac_decoder_param{
|
||||
int PcmWidth;
|
||||
};
|
||||
|
||||
struct pcm_decoder_param {
|
||||
uint32_t sample_rate;
|
||||
uint16_t frame_size;
|
||||
uint8_t channels;
|
||||
};
|
||||
|
||||
struct codec_decoder_api {
|
||||
void *(*init)(void *param);
|
||||
void (*destroy)(void *handle);
|
||||
int (*decode)(void *handle, const uint8_t *data, uint32_t *length, uint8_t **out_buf, uint32_t *out_length);
|
||||
int (*plc)(void *handle, uint8_t **out_buf, uint32_t *out_length);
|
||||
int (*get_param)(void *handle, uint32_t *sample_rate, uint8_t *channels);
|
||||
};
|
||||
|
||||
struct codec_decoder_handle {
|
||||
struct codec_decoder_api *api;
|
||||
void *decoder_env;
|
||||
};
|
||||
|
||||
struct codec_encoder_api {
|
||||
void *(*init)(void *param);
|
||||
void (*destroy)(void *handle);
|
||||
int (*encode)(void *handle, const uint8_t *data, uint32_t *length, uint8_t **out_buf, uint32_t *out_length);
|
||||
};
|
||||
|
||||
struct codec_encoder_handle {
|
||||
struct codec_encoder_api *api;
|
||||
void *encoder_env;
|
||||
};
|
||||
|
||||
struct codec_decoder_handle *codec_decoder_init(uint8_t decoder_type, void *param);
|
||||
void codec_decoder_destroy(struct codec_decoder_handle *handle);
|
||||
int codec_decoder_decode(struct codec_decoder_handle *handle,
|
||||
const uint8_t *in_buf,
|
||||
uint32_t *in_length,
|
||||
uint8_t **out_buf,
|
||||
uint32_t *out_length);
|
||||
int codec_decoder_plc(struct codec_decoder_handle *handle,
|
||||
uint8_t **out_buf,
|
||||
uint32_t *out_length);
|
||||
int codec_decoder_get_param(struct codec_decoder_handle *handle, uint32_t *sample_rate, uint8_t *channels);
|
||||
|
||||
struct codec_encoder_handle *codec_encoder_init(uint8_t encoder_type, void *param);
|
||||
void codec_encoder_destroy(struct codec_encoder_handle *handle);
|
||||
int codec_encoder_encode(struct codec_encoder_handle *handle,
|
||||
const uint8_t *in_buf,
|
||||
uint32_t *in_length,
|
||||
uint8_t **out_buf,
|
||||
uint32_t *out_length);
|
||||
int codec_encoder_plc(struct codec_encoder_handle *handle,
|
||||
uint8_t **out_buf,
|
||||
uint32_t *out_length);
|
||||
#endif /* _CODEC_H */
|
98
MCU/components/modules/audio/resample/resample.c
Normal file
98
MCU/components/modules/audio/resample/resample.c
Normal file
@ -0,0 +1,98 @@
|
||||
#include "audio_rpmsg.h"
|
||||
#include "resample.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
void *resample_init(enum resample_type type, uint8_t channels)
|
||||
{
|
||||
struct rpmsg_sync_msg_resample_init_t *sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_decoder_init_t));
|
||||
if (sync_msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
sync_msg->resample_type = type;
|
||||
sync_msg->channels = channels;
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_RESAMPLE_INIT, sync_msg, (uint32_t *)&result);
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void resample_destroy(void *handle)
|
||||
{
|
||||
struct rpmsg_sync_msg_resample_destroy_t sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
|
||||
if (handle == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
sync_msg.handle = handle;
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_RESAMPLE_DESTROY, (void *)&sync_msg, (uint32_t *)&result);
|
||||
}
|
||||
|
||||
int resample_exec(void *handle, const uint8_t *indata, uint32_t *insize, uint8_t **out_buf, uint32_t *out_length)
|
||||
{
|
||||
struct rpmsg_sync_msg_resample_exec_t *sync_msg;
|
||||
void *result;
|
||||
uint32_t ret;
|
||||
bool trans_addr = false;
|
||||
|
||||
sync_msg = pvPortMalloc(sizeof(struct rpmsg_sync_msg_encoder_exec_t));
|
||||
if (sync_msg == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (((uint32_t)indata >= 0x200A0000) && ((uint32_t)indata < 0x30000000)) {
|
||||
indata = (void *)((uint32_t)indata - 0x200A0000 + 0x781e0000);
|
||||
}
|
||||
|
||||
sync_msg->handle = handle;
|
||||
sync_msg->in_buffer = indata;
|
||||
sync_msg->in_length = insize;
|
||||
sync_msg->out_buffer = out_buf;
|
||||
sync_msg->out_length = out_length;
|
||||
|
||||
if (*out_buf == NULL) {
|
||||
trans_addr = true;
|
||||
}
|
||||
|
||||
ret = rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_RESAMPLE_EXEC, sync_msg, (uint32_t *)&result);
|
||||
|
||||
if (trans_addr) {
|
||||
*out_buf = (void *)(0x200A0000 + (uint32_t)*out_buf - 0x781e0000);
|
||||
}
|
||||
|
||||
vPortFree(sync_msg);
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
enum resample_type resample_get_type(uint32_t in_sample_rate, uint32_t out_sample_rate)
|
||||
{
|
||||
switch (in_sample_rate) {
|
||||
case 48000:
|
||||
switch (out_sample_rate) {
|
||||
case 44100:
|
||||
return RESAMPLE_TYPE_D_48000_44100;
|
||||
default:
|
||||
return RESAMPLE_TYPE_INVALID;
|
||||
}
|
||||
// case 44100:
|
||||
// switch (out_sample_rate) {
|
||||
// case 16000:
|
||||
// return RESAMPLE_TYPE_D_44100_16000;
|
||||
// default:
|
||||
// return RESAMPLE_TYPE_INVALID;
|
||||
// }
|
||||
default:
|
||||
return RESAMPLE_TYPE_INVALID;
|
||||
}
|
||||
}
|
||||
|
29
MCU/components/modules/audio/resample/resample.h
Normal file
29
MCU/components/modules/audio/resample/resample.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef _RESAMPLE_H
|
||||
#define _RESAMPLE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum resample_type {
|
||||
RESAMPLE_TYPE_D_48000_44100,
|
||||
RESAMPLE_TYPE_D_64000_8000,
|
||||
RESAMPLE_TYPE_D_64000_32000,
|
||||
RESAMPLE_TYPE_D_32000_16000,
|
||||
RESAMPLE_TYPE_D_16000_8000,
|
||||
RESAMPLE_TYPE_D_44100_16000,
|
||||
|
||||
RESAMPLE_TYPE_U_8000_16000,
|
||||
RESAMPLE_TYPE_U_8000_64000,
|
||||
RESAMPLE_TYPE_U_8000_16000_CVSD,
|
||||
RESAMPLE_TYPE_U_16000_32000,
|
||||
RESAMPLE_TYPE_U_32000_64000,
|
||||
|
||||
RESAMPLE_TYPE_INVALID,
|
||||
};
|
||||
|
||||
void *resample_init(enum resample_type type, uint8_t channels);
|
||||
void resample_destroy(void *handle);
|
||||
int resample_exec(void *handle, const uint8_t *indata, uint32_t *insize, uint8_t **out_buf, uint32_t *out_length);
|
||||
enum resample_type resample_get_type(uint32_t in_sample_rate, uint32_t out_sample_rate);
|
||||
|
||||
#endif // _RESAMPLE_H
|
||||
|
122
MCU/components/modules/audio/voice_recognize.c
Normal file
122
MCU/components/modules/audio/voice_recognize.c
Normal file
@ -0,0 +1,122 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "audio_scene.h"
|
||||
#include "audio_rpmsg.h"
|
||||
#include "dsp.h"
|
||||
#include "dsp_mem.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
#define VOICE_RECOGNIZE_PCM_BUFFER_MAX_SIZE 128
|
||||
|
||||
static void voice_recognize_launch(int16_t *mic_pcm, uint32_t samples)
|
||||
{
|
||||
struct rpmsg_sync_msg_voice_recognize_launch_t sync_msg;
|
||||
|
||||
if (((uint32_t)mic_pcm >= DSP_DRAM_MCU_BASE_ADDR) && ((uint32_t)mic_pcm < (DSP_DRAM_MCU_BASE_ADDR+DSP_DRAM_SIZE))) {
|
||||
mic_pcm = (void *)MCU_SRAM_2_DSP_DRAM(mic_pcm);
|
||||
}
|
||||
sync_msg.mic_pcm = mic_pcm;
|
||||
sync_msg.samples = samples;
|
||||
rpmsg_sync_invoke(rpmsg_get_remote_instance(), RPMSG_SYNC_FUNC_VOICE_RECOGNIZE_LAUNCH, (void *)&sync_msg, NULL);
|
||||
}
|
||||
|
||||
static void hw_receive_adc_pcm_cb(uint32_t samples)
|
||||
{
|
||||
audio_scene_evt_t *evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
|
||||
|
||||
if (evt) {
|
||||
evt->type = AUDIO_SCENE_EVT_TYPE_ADC_NEW_SAMPLES;
|
||||
evt->p.adc_new_samples = samples;
|
||||
audio_scene_send_event(evt);
|
||||
}
|
||||
}
|
||||
|
||||
static void voice_recognize_init(void)
|
||||
{
|
||||
voice_recognize_env_t *env;
|
||||
audio_scene_param_voice_recognize_t *param;
|
||||
|
||||
env = &audio_scene_env.env.voice_recognize;
|
||||
param = &audio_scene_env.scene->param.voice_recognize;
|
||||
|
||||
/* initialize audio hardware */
|
||||
env->audio_hw = audio_hw_create(param->hw_type,
|
||||
NULL,
|
||||
param->hw_base_addr,
|
||||
AUDIO_HW_DIR_IN,
|
||||
param->sample_rate,
|
||||
AUDIO_CHANNELS_MONO);
|
||||
env->audio_hw_output = audio_hw_output_add(env->audio_hw, hw_receive_adc_pcm_cb);
|
||||
|
||||
/* allocate PCM buffer to save MIC data */
|
||||
env->pcm = dsp_mem_alloc(sizeof(int16_t) * VOICE_RECOGNIZE_PCM_BUFFER_MAX_SIZE);
|
||||
|
||||
/* TODO, notice DSP to prepare for voice recognization */
|
||||
}
|
||||
|
||||
void voice_recognize_destroy(void)
|
||||
{
|
||||
if (audio_scene_env.scene
|
||||
&& (audio_scene_env.scene->type == AUDIO_SCENE_TYPE_VOICE_RECOGNIZE)) {
|
||||
voice_recognize_env_t *env = &audio_scene_env.env.voice_recognize;
|
||||
|
||||
/* release audio hardware */
|
||||
audio_hw_destroy(env->audio_hw);
|
||||
|
||||
/* release pcm buffer */
|
||||
dsp_mem_free(env->pcm);
|
||||
|
||||
/* TODO, notice DSP to release resources for voice recognization */
|
||||
}
|
||||
}
|
||||
|
||||
void voice_recognize_task(void *arg)
|
||||
{
|
||||
audio_scene_t *scene = audio_scene_env.scene;
|
||||
audio_scene_evt_t *evt;
|
||||
bool exit = false;
|
||||
|
||||
if (scene == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* initialize necessary audio decoder and hardware */
|
||||
voice_recognize_init();
|
||||
|
||||
while (exit == false) {
|
||||
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
|
||||
GLOBAL_INT_DISABLE();
|
||||
evt = (void *)co_list_pop_front(&audio_scene_env.evt_list);
|
||||
GLOBAL_INT_RESTORE();
|
||||
|
||||
if (evt) {
|
||||
switch (evt->type) {
|
||||
case AUDIO_SCENE_EVT_TYPE_ADC_NEW_SAMPLES:
|
||||
{
|
||||
voice_recognize_env_t *voice_recognize = &audio_scene_env.env.voice_recognize;
|
||||
uint32_t adc_samples = evt->p.adc_new_samples;
|
||||
|
||||
/* save adc data into framebuffer */
|
||||
while ( adc_samples ) {
|
||||
uint32_t samples = adc_samples > VOICE_RECOGNIZE_PCM_BUFFER_MAX_SIZE ? VOICE_RECOGNIZE_PCM_BUFFER_MAX_SIZE : adc_samples;
|
||||
|
||||
audio_hw_read_pcm(voice_recognize->audio_hw_output, &voice_recognize->pcm[0], samples, AUDIO_CHANNELS_MONO);
|
||||
voice_recognize_launch(&voice_recognize->pcm[0], samples);
|
||||
|
||||
adc_samples -= samples;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AUDIO_SCENE_EVT_TYPE_DESTROY:
|
||||
exit = true;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
vPortFree(evt);
|
||||
}
|
||||
}
|
||||
|
||||
audio_scene_task_destroy();
|
||||
}
|
26
MCU/components/modules/audio/voice_recognize.h
Normal file
26
MCU/components/modules/audio/voice_recognize.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef _VOICE_RECOGNIZE_H
|
||||
#define _VOICE_RECOGNIZE_H
|
||||
|
||||
#include "audio_scene.h"
|
||||
|
||||
typedef struct {
|
||||
audio_hw_type_t hw_type;
|
||||
uint32_t sample_rate;
|
||||
uint32_t hw_base_addr;
|
||||
} audio_scene_param_voice_recognize_t;
|
||||
|
||||
typedef struct {
|
||||
audio_hw_t *audio_hw;
|
||||
audio_hw_output_t *audio_hw_output;
|
||||
|
||||
/* used to save pcm data */
|
||||
int16_t *pcm;
|
||||
} voice_recognize_env_t;
|
||||
|
||||
/* called by audio_scene_create to start voice recognize scene */
|
||||
void voice_recognize_task(void *arg);
|
||||
|
||||
/* called by audio_scene_destroy to remove voice recognize*/
|
||||
void voice_recognize_destroy(void);
|
||||
|
||||
#endif // _VOICE_RECOGNIZE_H
|
Reference in New Issue
Block a user