1 /*
2 * Copyright 2020 Rockchip Electronics Co. LTD
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 #include <stdio.h>
19 #include <errno.h>
20 #include <cstring>
21 #include <cstdlib>
22 #include <unistd.h>
23 #include <pthread.h>
24 #include <sys/poll.h>
25 #include "rk_defines.h"
26 #include "rk_debug.h"
27 #include "rk_mpi_ai.h"
28 #include "rk_mpi_sys.h"
29 #include "rk_mpi_mb.h"
30
31 #include "test_comm_argparse.h"
32
33 static RK_BOOL gAiExit = RK_FALSE;
34 #define TEST_AI_WITH_FD 0
35
36 typedef struct _rkMpiAICtx {
37 const char *srcFilePath;
38 const char *dstFilePath;
39 RK_S32 s32LoopCount;
40 RK_S32 s32ChnNum;
41 RK_S32 s32DeviceSampleRate;
42 RK_S32 s32SampleRate;
43 RK_S32 s32DeviceChannel;
44 RK_S32 s32Channel;
45 RK_S32 s32BitWidth;
46 RK_S32 s32DevId;
47 RK_S32 s32PeriodCount;
48 RK_S32 s32PeriodSize;
49 char *chCardName;
50 RK_S32 s32ChnIndex;
51 RK_S32 s32DevFd;
52 } TEST_AI_CTX_S;
53
ai_find_sound_mode(RK_S32 ch)54 static AUDIO_SOUND_MODE_E ai_find_sound_mode(RK_S32 ch) {
55 AUDIO_SOUND_MODE_E channel = AUDIO_SOUND_MODE_BUTT;
56 switch (ch) {
57 case 1:
58 channel = AUDIO_SOUND_MODE_MONO;
59 break;
60 case 2:
61 channel = AUDIO_SOUND_MODE_STEREO;
62 break;
63 default:
64 RK_LOGE("channel = %d not support", ch);
65 return AUDIO_SOUND_MODE_BUTT;
66 }
67
68 return channel;
69 }
70
ai_find_bit_width(RK_S32 bit)71 static AUDIO_BIT_WIDTH_E ai_find_bit_width(RK_S32 bit) {
72 AUDIO_BIT_WIDTH_E bitWidth = AUDIO_BIT_WIDTH_BUTT;
73 switch (bit) {
74 case 8:
75 bitWidth = AUDIO_BIT_WIDTH_8;
76 break;
77 case 16:
78 bitWidth = AUDIO_BIT_WIDTH_16;
79 break;
80 case 24:
81 bitWidth = AUDIO_BIT_WIDTH_24;
82 break;
83 default:
84 RK_LOGE("bitwidth(%d) not support", bit);
85 return AUDIO_BIT_WIDTH_BUTT;
86 }
87
88 return bitWidth;
89 }
90
test_ai_poll_event(RK_S32 timeoutMsec,RK_S32 fd)91 RK_S32 test_ai_poll_event(RK_S32 timeoutMsec, RK_S32 fd) {
92 RK_S32 num_fds = 1;
93 struct pollfd pollFds[num_fds];
94 RK_S32 ret = 0;
95
96 RK_ASSERT(fd > 0);
97 memset(pollFds, 0, sizeof(pollFds));
98 pollFds[0].fd = fd;
99 pollFds[0].events = (POLLPRI | POLLIN | POLLERR | POLLNVAL | POLLHUP);
100
101 ret = poll(pollFds, num_fds, timeoutMsec);
102
103 if (ret > 0 && (pollFds[0].revents & (POLLERR | POLLNVAL | POLLHUP))) {
104 RK_LOGE("fd:%d polled error", fd);
105 return -1;
106 }
107
108 return ret;
109 }
110
test_open_device_ai(TEST_AI_CTX_S * ctx)111 RK_S32 test_open_device_ai(TEST_AI_CTX_S *ctx) {
112 AUDIO_DEV aiDevId = ctx->s32DevId;
113 AUDIO_SOUND_MODE_E soundMode;
114
115 AIO_ATTR_S aiAttr;
116 RK_S32 result;
117 memset(&aiAttr, 0, sizeof(AIO_ATTR_S));
118
119 if (ctx->chCardName) {
120 snprintf(reinterpret_cast<char *>(aiAttr.u8CardName),
121 sizeof(aiAttr.u8CardName), "%s", ctx->chCardName);
122 }
123
124 aiAttr.soundCard.channels = ctx->s32DeviceChannel;
125 aiAttr.soundCard.sampleRate = ctx->s32DeviceSampleRate;
126 aiAttr.soundCard.bitWidth = AUDIO_BIT_WIDTH_16;
127
128 AUDIO_BIT_WIDTH_E bitWidth = ai_find_bit_width(ctx->s32BitWidth);
129 if (bitWidth == AUDIO_BIT_WIDTH_BUTT) {
130 goto __FAILED;
131 }
132 aiAttr.enBitwidth = bitWidth;
133 aiAttr.enSamplerate = (AUDIO_SAMPLE_RATE_E)ctx->s32SampleRate;
134 soundMode = ai_find_sound_mode(ctx->s32Channel);
135 if (soundMode == AUDIO_SOUND_MODE_BUTT) {
136 goto __FAILED;
137 }
138 aiAttr.enSoundmode = soundMode;
139 aiAttr.u32FrmNum = ctx->s32PeriodCount;
140 aiAttr.u32PtNumPerFrm = ctx->s32PeriodSize;
141
142 aiAttr.u32EXFlag = 0;
143 aiAttr.u32ChnCnt = 2;
144
145 result = RK_MPI_AI_SetPubAttr(aiDevId, &aiAttr);
146 if (result != 0) {
147 RK_LOGE("ai set attr fail, reason = %d", result);
148 goto __FAILED;
149 }
150
151 result = RK_MPI_AI_Enable(aiDevId);
152 if (result != 0) {
153 RK_LOGE("ai enable fail, reason = %d", result);
154 goto __FAILED;
155 }
156
157 return RK_SUCCESS;
158 __FAILED:
159 return RK_FAILURE;
160 }
161
test_init_mpi_ai(TEST_AI_CTX_S * params)162 RK_S32 test_init_mpi_ai(TEST_AI_CTX_S *params) {
163 RK_S32 result;
164
165 result = RK_MPI_AI_EnableChn(params->s32DevId, params->s32ChnIndex);
166 if (result != 0) {
167 RK_LOGE("ai enable channel fail, aoChn = %d, reason = %x", params->s32ChnIndex, result);
168 return RK_FAILURE;
169 }
170
171 #if TEST_AI_WITH_FD
172 // open fd immediate after enable chn will be better.
173 params->s32DevFd = RK_MPI_AI_GetFd(params->s32DevId, params->s32ChnIndex);
174 RK_LOGI("ai (devId: %d, chnId: %d), selectFd:%d", params->s32DevId, params->s32ChnIndex, params->s32DevFd);
175 #endif
176
177 result = RK_MPI_AI_EnableReSmp(params->s32DevId, params->s32ChnIndex,
178 (AUDIO_SAMPLE_RATE_E)params->s32SampleRate);
179 if (result != 0) {
180 RK_LOGE("ai enable channel fail, reason = %x, aoChn = %d", result, params->s32ChnIndex);
181 return RK_FAILURE;
182 }
183
184 return RK_SUCCESS;
185 }
186
test_deinit_mpi_ai(TEST_AI_CTX_S * params)187 RK_S32 test_deinit_mpi_ai(TEST_AI_CTX_S *params) {
188 RK_MPI_AI_DisableReSmp(params->s32DevId, params->s32ChnIndex);
189 RK_S32 result = RK_MPI_AI_DisableChn(params->s32DevId, params->s32ChnIndex);
190 if (result != 0) {
191 RK_LOGE("ai disable channel fail, reason = %d", result);
192 return RK_FAILURE;
193 }
194
195 result = RK_MPI_AI_Disable(params->s32DevId);
196 if (result != 0) {
197 RK_LOGE("ai disable fail, reason = %d", result);
198 return RK_FAILURE;
199 }
200
201 return RK_SUCCESS;
202 }
203
sendDataThread(void * ptr)204 void* sendDataThread(void * ptr) {
205 TEST_AI_CTX_S *params = reinterpret_cast<TEST_AI_CTX_S *>(ptr);
206
207 RK_S32 result = 0;
208 RK_S32 s32MilliSec = -1;
209 AUDIO_FRAME_S frame;
210
211 if (params->dstFilePath) {
212 AUDIO_SAVE_FILE_INFO_S save;
213 save.bCfg = RK_TRUE;
214 save.u32FileSize = 1024;
215 snprintf(save.aFilePath, sizeof(save.aFilePath), "%s", params->dstFilePath);
216 snprintf(save.aFileName, sizeof(save.aFileName), "%s", "cap_out.pcm");
217 RK_MPI_AI_SaveFile(params->s32DevId, params->s32ChnIndex, &save);
218 }
219
220 while (!gAiExit) {
221 #if TEST_AI_WITH_FD
222 test_ai_poll_event(-1, params->s32DevFd);
223 #endif
224 result = RK_MPI_AI_GetFrame(params->s32DevId, params->s32ChnIndex, &frame, RK_NULL, s32MilliSec);
225 if (result == 0) {
226 void* data = RK_MPI_MB_Handle2VirAddr(frame.pMbBlk);
227 // get the length of valid data in this frame
228 RK_U64 len = RK_MPI_MB_GetLength(frame.pMbBlk);
229 RK_LOGV("data = %p, len = %lld", data, len);
230 RK_MPI_AI_ReleaseFrame(params->s32DevId, params->s32ChnIndex, &frame, RK_NULL);
231 }
232 }
233
234 return RK_NULL;
235 }
236
unit_test_mpi_ai(TEST_AI_CTX_S * ctx)237 RK_S32 unit_test_mpi_ai(TEST_AI_CTX_S *ctx) {
238 RK_S32 i = 0;
239 TEST_AI_CTX_S params[AI_MAX_CHN_NUM];
240 pthread_t tidSend[AI_MAX_CHN_NUM];
241 pthread_t tidComand[AI_MAX_CHN_NUM];
242
243 if (test_open_device_ai(ctx) != RK_SUCCESS) {
244 goto __FAILED;
245 }
246
247 for (i = 0; i < ctx->s32ChnNum; i++) {
248 memcpy(&(params[i]), ctx, sizeof(TEST_AI_CTX_S));
249 params[i].s32ChnIndex = i;
250 params[i].s32DevFd = -1;
251
252 test_init_mpi_ai(¶ms[i]);
253 pthread_create(&tidSend[i], RK_NULL, sendDataThread, reinterpret_cast<void *>(¶ms[i]));
254 }
255
256 for (i = 0; i < ctx->s32ChnNum; i++) {
257 pthread_join(tidSend[i], RK_NULL);
258 pthread_join(tidComand[i], RK_NULL);
259 test_deinit_mpi_ai(¶ms[i]);
260 }
261
262 return RK_SUCCESS;
263 __FAILED:
264
265 return RK_FAILURE;
266 }
267
mpi_ai_test_show_options(const TEST_AI_CTX_S * ctx)268 static void mpi_ai_test_show_options(const TEST_AI_CTX_S *ctx) {
269 RK_PRINT("cmd parse result:\n");
270 RK_PRINT("input file name : %s\n", ctx->srcFilePath);
271 RK_PRINT("output file name : %s\n", ctx->dstFilePath);
272 RK_PRINT("loop count : %d\n", ctx->s32LoopCount);
273 RK_PRINT("channel number : %d\n", ctx->s32ChnNum);
274 RK_PRINT("open sound rate : %d\n", ctx->s32DeviceSampleRate);
275 RK_PRINT("record data rate : %d\n", ctx->s32SampleRate);
276 RK_PRINT("sound card channel : %d\n", ctx->s32DeviceChannel);
277 RK_PRINT("output channel : %d\n", ctx->s32Channel);
278 RK_PRINT("bit_width : %d\n", ctx->s32BitWidth);
279 RK_PRINT("period_count : %d\n", ctx->s32PeriodCount);
280 RK_PRINT("period_size : %d\n", ctx->s32PeriodSize);
281 RK_PRINT("sound card name : %s\n", ctx->chCardName);
282 RK_PRINT("device id : %d\n", ctx->s32DevId);
283 }
284
285 static const char *const usages[] = {
286 "./rk_mpi_ai_test [--device_rate rate] [--device_ch ch] [--out_rate rate] [--out_ch ch]...",
287 NULL,
288 };
289
main(int argc,const char ** argv)290 int main(int argc, const char **argv) {
291 RK_S32 i;
292 RK_S32 s32Ret;
293 TEST_AI_CTX_S *ctx;
294
295 ctx = reinterpret_cast<TEST_AI_CTX_S *>(malloc(sizeof(TEST_AI_CTX_S)));
296 memset(ctx, 0, sizeof(TEST_AI_CTX_S));
297
298 ctx->srcFilePath = RK_NULL;
299 ctx->dstFilePath = RK_NULL;
300 ctx->s32LoopCount = 1;
301 ctx->s32ChnNum = 1;
302 ctx->s32BitWidth = 16;
303 ctx->s32PeriodCount = 4;
304 ctx->s32PeriodSize = 1024;
305 ctx->chCardName = RK_NULL;
306 ctx->s32DevId = 0;
307
308 struct argparse_option options[] = {
309 OPT_HELP(),
310 OPT_GROUP("basic options:"),
311
312 OPT_INTEGER('\0', "device_rate", &(ctx->s32DeviceSampleRate),
313 "the sample rate of open sound card. <required>", NULL, 0, 0),
314 OPT_INTEGER('\0', "device_ch", &(ctx->s32DeviceChannel),
315 "the number of sound card channels. <required>.", NULL, 0, 0),
316 OPT_INTEGER('\0', "out_ch", &(ctx->s32Channel),
317 "the channels of out data. <required>", NULL, 0, 0),
318 OPT_INTEGER('\0', "out_rate", &(ctx->s32SampleRate),
319 "the sample rate of out data. <required>", NULL, 0, 0),
320 OPT_STRING('o', "output", &(ctx->dstFilePath),
321 "output file name, e.g.(./ai). default(NULL).", NULL, 0, 0),
322 OPT_INTEGER('n', "loop_count", &(ctx->s32LoopCount),
323 "loop running count. can be any count. default(1)", NULL, 0, 0),
324 OPT_INTEGER('c', "channel_count", &(ctx->s32ChnNum),
325 "the count of adec channel. default(1).", NULL, 0, 0),
326 OPT_INTEGER('\0', "bit", &(ctx->s32BitWidth),
327 "the bit width of open sound card, range(8, 16, 24), default(16)", NULL, 0, 0),
328 OPT_INTEGER('\0', "period_size", &(ctx->s32PeriodSize),
329 "the period size for open sound card, default(1024)", NULL, 0, 0),
330 OPT_INTEGER('\0', "period_count", &(ctx->s32PeriodCount),
331 "the period count for open sound card, default(4)", NULL, 0, 0),
332 OPT_STRING('\0', "sound_card_name", &(ctx->chCardName),
333 "the sound name for open sound card, default(NULL)", NULL, 0, 0),
334 OPT_END(),
335 };
336
337 struct argparse argparse;
338 argparse_init(&argparse, options, usages, 0);
339 argparse_describe(&argparse, "\nselect a test case to run.",
340 "\nuse --help for details.");
341
342 argc = argparse_parse(&argparse, argc, argv);
343 mpi_ai_test_show_options(ctx);
344
345 if (ctx->s32Channel <= 0
346 || ctx->s32SampleRate <= 0
347 || ctx->s32DeviceSampleRate <= 0
348 || ctx->s32DeviceChannel <= 0) {
349 argparse_usage(&argparse);
350 goto __FAILED;
351 }
352
353 RK_MPI_SYS_Init();
354
355 for (i = 0; i < ctx->s32LoopCount; i++) {
356 RK_LOGI("start running loop count = %d", i);
357 s32Ret = unit_test_mpi_ai(ctx);
358 if (s32Ret != RK_SUCCESS) {
359 goto __FAILED;
360 }
361 RK_LOGI("end running loop count = %d", i);
362 }
363
364 __FAILED:
365 if (ctx) {
366 free(ctx);
367 ctx = RK_NULL;
368 }
369
370 RK_MPI_SYS_Exit();
371 return 0;
372 }
373