demo工程暂存 优化菜单界面UI和功能

This commit is contained in:
2024-04-29 16:32:24 +08:00
commit 330cd25cf1
3310 changed files with 2163318 additions and 0 deletions

View 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);
}

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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);
}
}

View 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

View 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;
}

View 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

View 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

View 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();
}

View 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

View 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, &param, 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, &param, 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, &param, 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, &param, 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(&param, 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");
}

View 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

View 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;
}

View 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 */

View 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;
}
}

View 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

View 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();
}

View 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