This section is just a code demonstration. Customers with HIFI5 authorization can obtain the source code packages of NatureDSP_Signal and xa_nnlib_api from Candence, which include the API documentation:
bool "my dsp asr"
depends on ARCH_DSP
select COMPONENTS_AW_AUDIO_SYSTEM
+ select COMPONENTS_XTENSA_HIFI5_NNLIB_LIBRARY
+ select COMPONENTS_XTENSA_HIFI5_VFPU_LIBRARY
default n
help
(The action of linking the acceleration library has been added in lichee/dsp/Makefile, so there is no need to add it again)
FFT reference code:
#include <math.h>
#include "NatureDSP_Signal.h"
#include "xa_nnlib_api.h"
#ifndef PI
#define PI (3.141592653f)
#endif
struct cplxf_t {
float r;
float i;
};
static inline void make_src_cplxf_from_record_buffer(struct cplxf_t *output, int16_t *input, int N, uint8_t chs, uint8_t ch_index)
{
int i = 0;
for (i = 0; i < N; i++) {
output[i].r = input[i * chs + ch_index];
output[i].i = 0.0f;
}
}
// N*3/4 *twdstep
static inline void make_twd_cplxf(struct cplxf_t *output, const int N, const int twdstep)
{
int n, m;
for (n = 0; n < (twdstep * N) / 4; n++) {
for (m = 0; m < 3; m++) {
float phi = 2 * PI * (m + 1) * n / (twdstep * N);
output[n * 3 + m].r = cosf(phi);
output[n * 3 + m].i = sinf(phi);
}
}
}
int get_max(const struct cplxf_t *input_cplxf, int N)
{
float max = 0.0f;
float index = 0;
int i = 0;
for (i = 0; i < N; i++) {
float cur = input_cplxf[i].r * input_cplxf[i].r + input_cplxf[i].i * input_cplxf[i].i;
if (cur > max) {
max = cur;
index = i;
}
}
return index;
}
#define FFT_SIZE (1024)
static struct cplxf_t g_tmp_cplxf[FFT_SIZE];
static int record_data_handler(struct cplxf_t *output_cplxf, const struct cplxf_t *input_cplxf, const struct cplxf_t *twd_cplxf, int N)
{
if (N != FFT_SIZE) {
printf("%s incorrect data length: %d\n", __func__, N);
return -1;
}
memcpy(g_tmp_cplxf, input_cplxf, sizeof(g_tmp_cplxf));
fft_cplxf_ie(
(complex_float *)output_cplxf,
(complex_float *)g_tmp_cplxf,
(const complex_float *)twd_cplxf,
1,
FFT_SIZE);
return 0;
}
Add in the recording code:
ret = AudioRecordSetup(pAudioRecord, rate, channels, bitwidth);
if (ret) {
printf("%s:%u error!\n", __func__, __LINE__);
// TODO
}
+ static struct cplxf_t g_input_cplxf[FFT_SIZE];
+ static struct cplxf_t g_output_cplxf[FFT_SIZE];
+ static struct cplxf_t g_twd_cplxf[FFT_SIZE];
+ int N = rate * MS_PER_FRAME / 1000;
+ int twdstep = 1;
+ int max_index = -1;
+ make_twd_cplxf(g_twd_cplxf, N, twdstep);
while (time_ms < record_time_ms) {
ret = AudioRecordRead(pAudioRecord, record_buffer, sizeof(record_buffer));
if (ret < 0) {
printf("%s:%u error!\n", __func__, __LINE__);
// TODO
}
time_ms += MS_PER_FRAME;
+ max_index = -1;
+ make_src_cplxf_from_record_buffer(g_input_cplxf, record_buffer, N, channels, 0);
+ if( !record_data_handler(g_output_cplxf, g_input_cplxf, g_twd_cplxf, N)) {
+ max_index = get_max(g_output_cplxf, N);
+ }
printf("%ums: read %d, max: %d\n", time_ms, ret, max_index);
}
Inter-core communication
The code is for reference only and does not contain actual business code
Add in Kconfig:
bool "my dsp asr"
depends on ARCH_DSP
select COMPONENTS_AW_AUDIO_SYSTEM
+ select COMPONENTS_RPDATA
select COMPONENTS_XTENSA_HIFI5_NNLIB_LIBRARY
select COMPONENTS_XTENSA_HIFI5_VFPU_LIBRARY
default n
help
Since inter-core communication requires the cooperation of the RV core, it is also necessary to write components for RV-side control and data reception. You can refer to the method of writing DSP algorithm components for writing: Kconfig:
menu "my rv asr"
config COMPONENTS_MY_RV_ASR
bool "my RV asr"
depends on !ARCH_DSP
select COMPONENTS_RPDATA
default n
help
to do
endmenu
(Note that it is !ARCH_DSP)
Other modifications are similar to those in the first section and will not be repeated; The menuconfig command on the rv side is mrtos_menuconfig. The rv side execution code does not require "rpccli dsp";
The RV side and the DSP side each create a processing thread;
DSP sends data to RV regularly, and RV sends running flags to DSP regularly;
RV modifies the value of g_run to 0, RV sends the value of g_run to DSP and then exits. DSP will also exit after receiving g_run.
DUMP data to PC
There is no file system on the DSP side, so the data needs to be sent to the RV core through inter-core communication, and then saved or sent by the RV core in some way;
On the RV core, you can use adb forward to transfer data to the PC in real time, or you can save the data to flash and then use adb pull to the PC;
The codes in this section are all run on the RV core ;
Existing packaging interfaces can be used:
// file_path和port只需指定一个即可,另一个填NULL或0
// 指定file_path表示保存数据到flash,指定port表示提供adb传输数据到PC
void *data_save_create(const char *name, const char *file_path, int port);
void data_save_destroy(void *_hdl);
int data_save_request(void *_hdl, void *data, int size, int timeout_ms);
// 需要保存后续数据到另一文件时调用,用于分割音频数据
int data_save_flush(void *_hdl, int timeout_ms);
You can copy lichee/rtos-components/aw/asr_demo/inc/data_save.h and lichee/rtos-components/aw/asr_demo/src/data_save.c to your own components, or use them directly when CONFIG_COMPONENTS_ASR_DEMO is selected;
Save to flash through standard file operation interface or save to flash by specifying file path in data_save component
Follow these steps to transfer data to PC via adb forward:
① Device side:
reboot(重启设备)
(等待设备重启完成)
adb shell af -p [代码中填写的port] -r
③ The device starts to call the data_save interface to create a channel and send data (refer to the first section to add start and stop transmission commands)