1 /*******************************************************************************
2 * Copyright (C) 2018 Cadence Design Systems, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to use this Software with Cadence processor cores only and
7 * not with any other processors and platforms, subject to
8 * the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included
11 * in all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
18 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 
21 ******************************************************************************/
22 
23 #define MODULE_TAG                      UTEST
24 
25 #include "xaf-utils-test.h"
26 
27 #include "audio/xa_vorbis_dec_api.h"
28 #include "audio/xa-mixer-api.h"
29 #include "audio/xa-audio-decoder-api.h"
30 
31 #include "xa_playback.h"
32 
33 #define PRINT_USAGE do { fprintf(stderr, "\nUsage: %s <input-file1> <input-file2>\n", argv[0]); \
34                          fprintf(stderr, "       Only .ogg and .pcm files are supported. \n"); \
35                          fprintf(stderr, "       Playback is configured @ 48kHz, 2 ch, 16 bits pcm. \n"); \
36                          fprintf(stderr, "       <input-file2> is optional. \n"); \
37                          fprintf(stderr, "       pcm output is written to dec-mix-out.pcm, by default. \n\n"); \
38                        } while(0)
39 
40 #define MAX_INP_STRMS       2
41 #define NUM_COMP_IN_GRAPH   3
42 
43 /* ...global variables */
44 int g_pthread_exit_code=0x12345678;
45 
46 /* ...playback format */
47 xaf_format_t pb_format;
48 
49 /* ...playback device parameters */
50 void *pb_handle = NULL;
51 
thread_exit_handler(int sig)52 void thread_exit_handler(int sig)
53 {
54     /* ...unused arg */
55     (void) sig;
56 
57     pthread_exit(0);
58 }
59 
vorbis_setup(void * p_decoder)60 static int vorbis_setup(void *p_decoder)
61 {
62     int param[2];
63 
64     /* 1: Raw decode, 0: Ogg decode */
65     param[0] = XA_VORBISDEC_CONFIG_PARAM_RAW_VORBIS_FILE_MODE;
66     param[1] = 0;
67 
68     XF_CHK_API(xaf_comp_set_config(p_decoder, 1, &param[0]));
69 
70     return 0;
71 }
72 
pcm_setup(void * p_pcm)73 static int pcm_setup(void *p_pcm)
74 {
75     int param[6];
76 
77     param[0] = XA_CODEC_CONFIG_PARAM_SAMPLE_RATE;
78     param[1] = pb_format.sample_rate;
79     param[2] = XA_CODEC_CONFIG_PARAM_CHANNELS;
80     param[3] = pb_format.channels;
81     param[4] = XA_CODEC_CONFIG_PARAM_PCM_WIDTH;
82     param[5] = pb_format.pcm_width;
83 
84     XF_CHK_API(xaf_comp_set_config(p_pcm, 3, &param[0]));
85 
86     return 0;
87 }
88 
mixer_setup(void * p_decoder,xaf_format_t * p_format)89 static int mixer_setup(void *p_decoder, xaf_format_t *p_format)
90 {
91     int param[6];
92 
93     param[0] = XA_MIXER_CONFIG_PARAM_SAMPLE_RATE;
94     param[1] = p_format->sample_rate;
95     param[2] = XA_MIXER_CONFIG_PARAM_CHANNELS;
96     param[3] = p_format->channels;
97     param[4] = XA_MIXER_CONFIG_PARAM_PCM_WIDTH;
98     param[5] = p_format->pcm_width;
99 
100     XF_CHK_API(xaf_comp_set_config(p_decoder, 3, &param[0]));
101 
102     return 0;
103 }
104 
consume_output(void * p_buf,int buf_length,void * p_output)105 static int consume_output(void *p_buf, int buf_length, void *p_output)
106 {
107     XAF_CHK_PTR(p_buf);
108     XAF_CHK_PTR(p_output);
109 
110 #if !defined BOARD
111     FILE *fp = p_output;
112     fwrite(p_buf, 1, buf_length, fp);
113 
114     if (xa_playback_buf(pb_handle, p_buf, buf_length)) {
115         TRACE(ERROR, _b("Playback Failed \n"));
116 	return -1;
117     }
118 #else
119 #endif
120     return 0;
121 }
122 
read_input(void * p_buf,int buf_length,int * read_length,void * p_input)123 static int read_input(void *p_buf, int buf_length, int *read_length, void *p_input)
124 {
125     XAF_CHK_PTR(p_buf);
126     XAF_CHK_PTR(read_length);
127     XAF_CHK_PTR(p_input);
128 
129 #if !defined BOARD
130     FILE *fp = p_input;
131     *read_length = fread(p_buf, 1, buf_length, fp);
132 #else
133 #endif
134     return 0;
135 }
136 
comp_process_entry(void * arg)137 static int comp_process_entry(void *arg)
138 {
139     void *p_comp;
140     void *p_input, *p_output;
141     xaf_comp_status comp_status;
142     xaf_info_t comp_info;
143     int input_over, read_length;
144     void * (*arg_arr)[3];
145     void *pg_pthread_exit_code = (void*)&g_pthread_exit_code;
146 
147     XAF_CHK_PTR(arg);
148 
149     arg_arr = arg;
150     p_comp   = (*arg_arr)[0];
151     p_input  = (*arg_arr)[1];
152     p_output = (*arg_arr)[2];
153     input_over = 0;
154 
155     XF_CHK_API(xaf_comp_process(NULL, p_comp, NULL, 0, XAF_EXEC_FLAG));
156 
157     while (1)
158     {
159         XF_CHK_API(xaf_comp_get_status(NULL, p_comp, &comp_status, &comp_info));
160 
161         if (comp_status == XAF_EXEC_DONE) break;
162 
163         if (comp_status == XAF_NEED_INPUT && !input_over)
164         {
165             void *p_buf = (void *) comp_info.buf;
166             int size    = comp_info.length;
167 
168             XF_CHK_API(read_input(p_buf, size, &read_length, p_input));
169 
170             if (read_length)
171                 XF_CHK_API(xaf_comp_process(NULL, p_comp, (void *)comp_info.buf, read_length, XAF_INPUT_READY_FLAG));
172             else
173             {
174                 XF_CHK_API(xaf_comp_process(NULL, p_comp, NULL, 0, XAF_INPUT_OVER_FLAG));
175                 input_over = 1;
176             }
177         }
178 
179         if (comp_status == XAF_OUTPUT_READY)
180         {
181             void *p_buf = (void *) comp_info.buf;
182             int size    = comp_info.length;
183 
184             XF_CHK_API(consume_output(p_buf, size, p_output));
185             XF_CHK_API(xaf_comp_process(NULL, p_comp, (void *)comp_info.buf, comp_info.length, XAF_NEED_OUTPUT_FLAG));
186         }
187     }
188     pthread_exit(pg_pthread_exit_code);
189 
190     return 0;
191 }
192 
main(int argc,const char ** argv)193 int main(int argc, const char **argv)
194 {
195     void *p_adev = NULL;
196     void *p_decoder[MAX_INP_STRMS];
197     void *p_mixer;
198     mem_obj_t* mem_handle;
199     int num_comp = NUM_COMP_IN_GRAPH;
200 
201     xaf_comp_status dec_status;
202     xaf_info_t comp_info;
203 
204     void *p_input[MAX_INP_STRMS], *p_output;
205 
206     xf_id_t dec_id[MAX_INP_STRMS];
207     int (*dec_setup[MAX_INP_STRMS])(void *p_comp);
208 
209     pthread_t dec_thread[MAX_INP_STRMS];
210     pthread_t mixer_thread;
211     void *dec_thread_args[MAX_INP_STRMS][3];
212     void *mixer_thread_args[3];
213 
214     const char *ext;
215     FILE *fp, *ofp;
216     void *dec_inbuf[MAX_INP_STRMS][2];
217     int buf_length = XAF_INBUF_SIZE;
218     int read_length;
219     int input_over = 0;
220     int i, j;
221     int num_strms;
222 
223     unsigned int card = 0;
224     unsigned int device = 0;
225     unsigned int period_size = 1024;
226     unsigned int period_count = 4;
227 
228     int pthread_error;
229     void *pthread_exit_code[3];
230 
231     struct sigaction actions;
232     memset(&actions, 0, sizeof(actions));
233     sigemptyset(&actions.sa_mask);
234     actions.sa_flags = 0;
235     actions.sa_handler = thread_exit_handler;
236     sigaction(SIGUSR1,&actions,NULL);
237 
238     /* ...initialize playback format */
239     pb_format.sample_rate = 48000;
240     pb_format.channels    = 2;
241     pb_format.pcm_width   = 16;
242 
243     audio_frmwk_buf_size = 0; //unused
244     audio_comp_buf_size  = 0; //unused
245 
246     print_banner("\'Audio decoder(PCM/Ogg-Vorbis) + Mixer\'");
247 
248     /* ...initialize tracing facility */
249     TRACE_INIT("Xtensa Audio Framework - Sample Application");
250 
251 #if !defined BOARD
252     /* ...check input arguments */
253     if (argc < 2 || argc > (MAX_INP_STRMS+1))
254     {
255         TRACE(ERROR, _b("Usage: ./xaf-test <infile1> <infile2>\n"));
256         PRINT_USAGE;
257         return 0;
258     }
259 
260     argc--;
261     for (i=0; i<argc; i++)
262     {
263         ext = strrchr(argv[i+1], '.');
264         if (!ext)
265         {
266             PRINT_USAGE;
267             return 0;
268         }
269         ext++;
270         if (!strcmp(ext, "pcm")) {
271      	    dec_id[i]    = "audio-decoder/pcm";
272             dec_setup[i] = pcm_setup;
273         }
274         else if (!strcmp(ext, "ogg")) {
275             dec_id[i]    = "audio-decoder/vorbis";
276             dec_setup[i] = vorbis_setup;
277         }
278         else {
279            TRACE(ERROR, _x("Unknown Decoder Extension '%s'"), ext);
280            PRINT_USAGE;
281            exit(-1);
282         }
283         /* ...open file */
284         if ((fp = fopen(argv[i+1], "rb")) == NULL)
285         {
286            TRACE(ERROR, _x("Failed to open '%s': %d"), argv[i+1], errno);
287            exit(-1);
288         }
289         p_input[i] = fp;
290     }
291     num_strms = i;
292 
293     if ((ofp = fopen("dec-mix-out.pcm", "wb")) == NULL)
294     {
295        TRACE(ERROR, _x("Failed to open '%s': %d"), "dec-mix-out.pcm", errno);
296        exit(-1);
297     }
298     p_output = ofp;
299 #endif
300 
301     mem_handle = mem_init(); //initialize memory handler
302 
303     XF_CHK_API(xaf_adev_open(&p_adev, audio_frmwk_buf_size, audio_comp_buf_size, mem_malloc, mem_free));
304 
305     /* ...create mixer component */
306     XF_CHK_API(xaf_comp_create(p_adev, &p_mixer, "mixer", 0, 1, NULL, XAF_MIXER));
307     XF_CHK_API(mixer_setup(p_mixer, &pb_format));
308 
309     for (i=0; i<num_strms; i++)
310     {
311         /* ...create decoder component */
312         XF_CHK_API(xaf_comp_create(p_adev, &p_decoder[i], dec_id[i], 2, 0, &dec_inbuf[i][0], XAF_DECODER));
313         XF_CHK_API((dec_setup[i])(p_decoder[i]));
314 
315     	/* ...start decoder component */
316         XF_CHK_API(xaf_comp_process(p_adev, p_decoder[i], NULL, 0, XAF_START_FLAG));
317 
318 	/* ...feed input to decoder component */
319         for (j=0; j<2; j++)
320         {
321             XF_CHK_API(read_input(dec_inbuf[i][j], buf_length, &read_length, p_input[i]));
322 
323             if (read_length)
324                 XF_CHK_API(xaf_comp_process(p_adev, p_decoder[i], dec_inbuf[i][j], read_length, XAF_INPUT_READY_FLAG));
325             else
326                 break;
327         }
328 
329     	/* ...initialization loop */
330         while (1)
331         {
332             XF_CHK_API(xaf_comp_get_status(p_adev, p_decoder[i], &dec_status, &comp_info));
333 
334             if (dec_status == XAF_INIT_DONE || dec_status == XAF_EXEC_DONE) break;
335 
336             if (dec_status == XAF_NEED_INPUT && !input_over)
337             {
338                 void *p_buf = (void *) comp_info.buf;
339                 int size    = comp_info.length;
340 
341                 XF_CHK_API(read_input(p_buf, size, &read_length, p_input[i]));
342 
343                 if (read_length)
344                     XF_CHK_API(xaf_comp_process(p_adev, p_decoder[i], p_buf, read_length, XAF_INPUT_READY_FLAG));
345                 else
346                     break;
347             }
348         }
349 
350         if (dec_status != XAF_INIT_DONE)
351         {
352             TRACE(ERROR, _x("Failed to init"));
353             exit(-1);
354         }
355 
356         XF_CHK_API(xaf_connect(p_decoder[i], p_mixer, 4));
357     }
358 
359     XF_CHK_API(xaf_comp_process(p_adev, p_mixer, NULL, 0, XAF_START_FLAG));
360     XF_CHK_API(xaf_comp_get_status(p_adev, p_mixer, &dec_status, &comp_info));
361 
362     if (dec_status != XAF_INIT_DONE)
363     {
364         TRACE(ERROR, _x("Failed to init"));
365         exit(-1);
366     }
367 
368     /* ...open playback device */
369     pb_handle = xa_playback_open(card, device, pb_format.channels, pb_format.sample_rate,
370 		  	         pb_format.pcm_width, period_size, period_count);
371     if (!pb_handle) {
372         TRACE(ERROR, _x("Playback open error\n"));
373 	return -1;
374     }
375 
376     for (i=0; i<num_strms; i++)
377     {
378         dec_thread_args[i][0] = p_decoder[i];
379         dec_thread_args[i][1] = p_input[i];
380         dec_thread_args[i][2] = p_output;
381         pthread_create(&dec_thread[i], 0, (void *(*)(void*))&comp_process_entry, dec_thread_args[i]);
382     }
383 
384     mixer_thread_args[0] = p_mixer;
385     mixer_thread_args[1] = NULL;
386     mixer_thread_args[2] = p_output;
387     pthread_create(&mixer_thread, 0, (void *(*)(void*))comp_process_entry, &mixer_thread_args[0]);
388 
389     for (i=0; i<num_strms; i++)
390     {
391       pthread_error = pthread_join(dec_thread[i], (void **) &pthread_exit_code[i]);
392       if(pthread_error)
393       {
394         TRACE(ERROR, _b("decode thread %d join error:%x\n"), i, pthread_error);
395       }
396     }
397     pthread_error = pthread_join(mixer_thread, (void **) &pthread_exit_code[i]);
398     if(pthread_error)
399     {
400       TRACE(ERROR, _b("mixer thread join error:%x\n"), pthread_error);
401     }
402 
403     for (i=0; i<num_strms; i++)
404     {
405         XF_CHK_API(xaf_comp_delete(p_decoder[i]));
406         if (p_input[i]) fclose(p_input[i]);
407     }
408     XF_CHK_API(xaf_comp_delete(p_mixer));
409 
410     /* ...exec done, clean-up */
411     xa_playback_close(pb_handle);
412 
413     XF_CHK_API(xaf_adev_close(p_adev, 0 /*unused*/));
414     if (p_output) fclose(p_output);
415 
416     mem_exit();
417     XF_CHK_API(print_mem_mcps_info(mem_handle, num_comp));
418 
419     return 0;
420 }
421 
422