2023-08-09 04:21:30 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* apps/testing/drivertest/drivertest_audio.c
|
|
|
|
*
|
|
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
|
|
* this work for additional information regarding copyright ownership. The
|
|
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
|
|
* "License"); you may not use this file except in compliance with the
|
|
|
|
* License. You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Included Files
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/time.h>
|
2023-08-15 19:18:05 +02:00
|
|
|
#include <sys/params.h>
|
2023-08-09 04:21:30 +02:00
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <setjmp.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <cmocka.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <mqueue.h>
|
|
|
|
#include <debug.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <nuttx/audio/audio.h>
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Pre-processor Definitions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#define OPTARG_TO_VALUE(value, type, base) \
|
|
|
|
do \
|
|
|
|
{ \
|
|
|
|
FAR char *ptr; \
|
|
|
|
value = (type)strtoul(optarg, &ptr, base); \
|
|
|
|
if (*ptr != '\0') \
|
|
|
|
{ \
|
|
|
|
printf("Parameter error: %s\n", optarg); \
|
|
|
|
audio_test_help(argv[0], EXIT_FAILURE); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Types
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
struct audio_state_s
|
|
|
|
{
|
|
|
|
char outfile[PATH_MAX];
|
|
|
|
char outdev[PATH_MAX];
|
|
|
|
char indev[PATH_MAX];
|
|
|
|
int outdev_fd;
|
|
|
|
int indev_fd;
|
|
|
|
int out_fd;
|
|
|
|
|
|
|
|
uint32_t samprate;
|
|
|
|
uint32_t format;
|
|
|
|
uint32_t bpsamp;
|
|
|
|
uint32_t chans;
|
|
|
|
uint32_t chmap;
|
|
|
|
|
|
|
|
char mqname[16];
|
|
|
|
int direction;
|
|
|
|
uint32_t duration;
|
|
|
|
uint32_t idx;
|
|
|
|
mqd_t mq;
|
|
|
|
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
|
|
FAR void *session;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: audio_test_writebuffer
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int audio_test_writebuffer(FAR struct audio_state_s *state,
|
|
|
|
FAR struct ap_buffer_s *apb)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (state->out_fd == -1)
|
|
|
|
{
|
|
|
|
return -ENODATA;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = write(state->out_fd, apb->samp, apb->nbytes);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
apb->curbyte = 0;
|
|
|
|
apb->flags = 0;
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: player_readbuffer
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int audio_test_readbuffer(FAR struct audio_state_s *state,
|
|
|
|
FAR struct ap_buffer_s *apb)
|
|
|
|
{
|
|
|
|
if (state->out_fd == -1)
|
|
|
|
{
|
|
|
|
return -ENODATA;
|
|
|
|
}
|
|
|
|
|
|
|
|
apb->nbytes = read(state->out_fd, apb->samp, apb->nmaxbytes);
|
|
|
|
apb->curbyte = 0;
|
|
|
|
apb->flags = 0;
|
|
|
|
|
|
|
|
if (apb->nbytes < apb->nmaxbytes)
|
|
|
|
{
|
|
|
|
close(state->out_fd);
|
|
|
|
state->out_fd = -1;
|
|
|
|
apb->flags |= AUDIO_APB_FINAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: audio_test_enqueuebuffer
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int audio_test_enqueuebuffer(FAR struct audio_state_s *state,
|
|
|
|
FAR struct ap_buffer_s *apb,
|
|
|
|
int direction)
|
|
|
|
{
|
|
|
|
struct audio_buf_desc_s bufdesc;
|
|
|
|
int ret = 0;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = direction == AUDIO_TYPE_OUTPUT ?
|
|
|
|
state->outdev_fd :
|
|
|
|
state->indev_fd;
|
|
|
|
|
|
|
|
apb->nbytes = apb->nmaxbytes;
|
|
|
|
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
|
|
bufdesc.session = state->session;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bufdesc.numbytes = apb->nbytes;
|
|
|
|
bufdesc.u.buffer = apb;
|
|
|
|
|
|
|
|
ret = ioctl(fd, AUDIOIOC_ENQUEUEBUFFER, (unsigned long)&bufdesc);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int audio_test_alloc_buffer(FAR struct audio_state_s *state,
|
|
|
|
FAR struct ap_buffer_s **bufs,
|
|
|
|
FAR struct ap_buffer_info_s *buf_info,
|
|
|
|
int direction)
|
|
|
|
{
|
|
|
|
struct audio_buf_desc_s buf_desc;
|
|
|
|
int ret = 0;
|
|
|
|
int fd;
|
|
|
|
int x;
|
|
|
|
|
|
|
|
fd = direction == AUDIO_TYPE_OUTPUT ?
|
|
|
|
state->outdev_fd :
|
|
|
|
state->indev_fd;
|
|
|
|
|
|
|
|
for (x = 0; x < buf_info->nbuffers; x++)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
|
|
buf_desc.session = state->session;
|
|
|
|
#endif
|
|
|
|
buf_desc.numbytes = buf_info->buffer_size;
|
|
|
|
buf_desc.u.pbuffer = &bufs[x];
|
|
|
|
|
|
|
|
ret = ioctl(fd, AUDIOIOC_ALLOCBUFFER, (unsigned long)&buf_desc);
|
|
|
|
if (ret != sizeof(buf_desc))
|
|
|
|
{
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
|
|
|
err_out:
|
|
|
|
free(bufs);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int audio_test_free_buffer(FAR struct audio_state_s *state,
|
|
|
|
FAR struct ap_buffer_s **bufs,
|
|
|
|
FAR struct ap_buffer_info_s *buf_info,
|
|
|
|
int direction)
|
|
|
|
{
|
|
|
|
struct audio_buf_desc_s buf_desc;
|
|
|
|
int ret = 0;
|
|
|
|
int fd;
|
|
|
|
int x;
|
|
|
|
|
|
|
|
fd = direction == AUDIO_TYPE_OUTPUT ?
|
|
|
|
state->outdev_fd :
|
|
|
|
state->indev_fd;
|
|
|
|
|
|
|
|
for (x = 0; x < buf_info->nbuffers; x++)
|
|
|
|
{
|
|
|
|
if (bufs[x] != NULL)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
|
|
buf_desc.session = state->session;
|
|
|
|
#endif
|
|
|
|
buf_desc.u.buffer = bufs[x];
|
|
|
|
ret = ioctl(fd, AUDIOIOC_FREEBUFFER, (unsigned long)&buf_desc);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void audio_test_help(FAR const char *progname, int exitcode)
|
|
|
|
{
|
|
|
|
printf("Usage: %s\n"
|
|
|
|
" -a <test case>\n"
|
|
|
|
" -t <duration>\n"
|
|
|
|
" -i <input device e.g./dev/audio/pcm0c>\n"
|
|
|
|
" -o <output device e.g./dev/audio/pcm0p>\n"
|
|
|
|
" -p <output file>\n"
|
|
|
|
" -f <format>\n"
|
|
|
|
" -b <bytes per sample> \n"
|
|
|
|
" -s <sample rate> \n"
|
|
|
|
" -c <channles> \n",
|
|
|
|
progname);
|
|
|
|
printf(" [-a testcase] selects the testcase\n"
|
|
|
|
" Case 1: Capture\n"
|
|
|
|
" Case 2: Playack\n"
|
|
|
|
" Case 3: First Capture, and then Playback\n"
|
|
|
|
);
|
|
|
|
printf(" -h shows this message and exits\n");
|
|
|
|
|
|
|
|
exit(exitcode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: parse_commandline
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static void parse_commandline(FAR struct audio_state_s *state, int argc,
|
|
|
|
FAR char **argv)
|
|
|
|
{
|
|
|
|
int option;
|
|
|
|
|
|
|
|
while ((option = getopt(argc, argv, "a:t::o::i::f::p:c::b::s::")) != ERROR)
|
|
|
|
{
|
|
|
|
switch (option)
|
|
|
|
{
|
|
|
|
case 'a':
|
|
|
|
{
|
|
|
|
OPTARG_TO_VALUE(state->direction, uint32_t, 10);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
{
|
|
|
|
OPTARG_TO_VALUE(state->duration, uint32_t, 10);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'o':
|
|
|
|
{
|
|
|
|
strcpy(state->outdev, optarg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'i':
|
|
|
|
{
|
|
|
|
strcpy(state->indev, optarg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'f':
|
|
|
|
{
|
|
|
|
if (!strcmp(optarg, "mp3"))
|
|
|
|
{
|
|
|
|
state->format = AUDIO_FMT_MP3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
state->format = AUDIO_FMT_PCM;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
{
|
|
|
|
strcpy(state->outfile, optarg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'c':
|
|
|
|
{
|
|
|
|
OPTARG_TO_VALUE(state->chans, uint32_t, 10);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'b':
|
|
|
|
{
|
|
|
|
OPTARG_TO_VALUE(state->bpsamp, uint32_t, 10);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
{
|
|
|
|
OPTARG_TO_VALUE(state->samprate, uint32_t, 10);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '?':
|
|
|
|
printf("Unknown option: %c\n", optopt);
|
|
|
|
audio_test_help(argv[0], EXIT_FAILURE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: audio_test_prepare
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int audio_test_prepare(FAR struct audio_state_s *state,
|
|
|
|
FAR struct ap_buffer_info_s *buf_info,
|
|
|
|
int direction)
|
|
|
|
{
|
|
|
|
struct audio_caps_desc_s cap_desc;
|
|
|
|
struct audio_caps_s caps;
|
|
|
|
int ret = 0;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = direction == AUDIO_TYPE_OUTPUT ?
|
|
|
|
state->outdev_fd :
|
|
|
|
state->indev_fd;
|
|
|
|
|
|
|
|
if (direction == AUDIO_TYPE_OUTPUT)
|
|
|
|
{
|
|
|
|
state->out_fd = open(state->outfile, O_RDONLY);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
state->out_fd = open(state->outfile, O_WRONLY | O_CREAT | O_TRUNC);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->out_fd == -1)
|
|
|
|
{
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
caps.ac_len = sizeof(caps);
|
|
|
|
caps.ac_type = AUDIO_TYPE_QUERY;
|
|
|
|
caps.ac_subtype = AUDIO_TYPE_QUERY;
|
|
|
|
|
|
|
|
ret = ioctl(fd, AUDIOIOC_GETCAPS, (unsigned long)&caps);
|
|
|
|
if (ret != caps.ac_len)
|
|
|
|
{
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((caps.ac_controls.b[0] & direction) == 0 ||
|
|
|
|
(caps.ac_format.hw & (1 << (state->format - 1))) == 0)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
|
|
ret = ioctl(fd, AUDIOIOC_RESERVE, (unsigned long)&state->session);
|
|
|
|
#else
|
|
|
|
ret = ioctl(fd, AUDIOIOC_RESERVE, 0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
|
|
cap_desc.session = state->session:
|
|
|
|
#endif
|
|
|
|
cap_desc.caps.ac_len = sizeof(struct audio_caps_s);
|
|
|
|
cap_desc.caps.ac_type = direction;
|
|
|
|
cap_desc.caps.ac_channels = state->chans;
|
|
|
|
cap_desc.caps.ac_chmap = state->chmap;
|
|
|
|
cap_desc.caps.ac_controls.hw[0] = state->samprate;
|
|
|
|
cap_desc.caps.ac_controls.b[3] = state->samprate >> 16;
|
|
|
|
cap_desc.caps.ac_controls.b[2] = state->bpsamp;
|
|
|
|
cap_desc.caps.ac_subtype = state->format;
|
|
|
|
ret = ioctl(fd, AUDIOIOC_CONFIGURE, (unsigned long)&cap_desc);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ioctl(fd, AUDIOIOC_REGISTERMQ, (unsigned long)state->mq);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ioctl(fd, AUDIOIOC_GETBUFFERINFO, (unsigned long)buf_info);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
buf_info->buffer_size = CONFIG_AUDIO_BUFFER_NUMBYTES;
|
|
|
|
buf_info->nbuffers = CONFIG_AUDIO_NUM_BUFFERS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int audio_test_start(FAR struct audio_state_s *state, int direction)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = direction == AUDIO_TYPE_OUTPUT ?
|
|
|
|
state->outdev_fd :
|
|
|
|
state->indev_fd;
|
|
|
|
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
|
|
ret = ioctl(fd, AUDIOIOC_START,
|
|
|
|
(unsigned long)state->session);
|
|
|
|
#else
|
|
|
|
ret = ioctl(fd, AUDIOIOC_START, 0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool audio_test_timeout(FAR struct audio_state_s *state,
|
|
|
|
int direction, struct timeval start)
|
|
|
|
{
|
|
|
|
struct timeval now;
|
|
|
|
struct timeval delta;
|
|
|
|
struct timeval wait;
|
|
|
|
|
|
|
|
if (direction == AUDIO_TYPE_OUTPUT)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wait.tv_sec = state->duration;
|
|
|
|
wait.tv_usec = 0;
|
|
|
|
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
timersub(&now, &start, &delta);
|
|
|
|
return timercmp(&delta, &wait, >);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int audio_test_stop(FAR struct audio_state_s *state, int direction)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = direction == AUDIO_TYPE_OUTPUT ?
|
|
|
|
state->outdev_fd :
|
|
|
|
state->indev_fd;
|
|
|
|
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
|
|
ret = ioctl(fd, AUDIOIOC_STOP,
|
|
|
|
(unsigned long)state->session);
|
|
|
|
#else
|
|
|
|
ret = ioctl(fd, AUDIOIOC_STOP, 0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int audio_test_cleanup(FAR struct audio_state_s *state, int direction)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = direction == AUDIO_TYPE_OUTPUT ?
|
|
|
|
state->outdev_fd :
|
|
|
|
state->indev_fd;
|
|
|
|
|
|
|
|
ioctl(fd, AUDIOIOC_UNREGISTERMQ, (unsigned long)state->mq);
|
|
|
|
|
|
|
|
if (state->out_fd >= 0)
|
|
|
|
{
|
|
|
|
close(state->out_fd);
|
|
|
|
state->out_fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void audio_test_case(void **audio_state)
|
|
|
|
{
|
|
|
|
FAR struct audio_state_s *state;
|
|
|
|
FAR struct ap_buffer_s **bufs = NULL;
|
|
|
|
struct ap_buffer_info_s buf_info;
|
|
|
|
struct audio_msg_s msg;
|
|
|
|
struct timeval start;
|
|
|
|
int directions[3] =
|
|
|
|
{
|
|
|
|
AUDIO_TYPE_INPUT,
|
|
|
|
AUDIO_TYPE_OUTPUT,
|
|
|
|
-1
|
|
|
|
};
|
|
|
|
|
|
|
|
struct mq_attr attr;
|
|
|
|
unsigned int prio;
|
|
|
|
bool streaming;
|
|
|
|
bool running;
|
|
|
|
int ret = 0;
|
|
|
|
int direct;
|
|
|
|
int x = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
state = (struct audio_state_s *)*audio_state;
|
|
|
|
|
|
|
|
while (directions[x] != -1)
|
|
|
|
{
|
|
|
|
/* first round, test capture.
|
|
|
|
* second round, test playback.
|
|
|
|
* */
|
|
|
|
|
|
|
|
streaming = running = true;
|
|
|
|
if (!(directions[x] & state->direction))
|
|
|
|
{
|
|
|
|
x++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
direct = directions[x];
|
|
|
|
|
|
|
|
ret = audio_test_prepare(state, &buf_info, direct);
|
|
|
|
assert_false(ret < 0);
|
|
|
|
|
|
|
|
bufs = (FAR struct ap_buffer_s **)
|
|
|
|
calloc(buf_info.nbuffers, sizeof(FAR void *));
|
|
|
|
assert_true(bufs != NULL);
|
|
|
|
|
|
|
|
ret = audio_test_alloc_buffer(state, bufs, &buf_info, direct);
|
|
|
|
assert_false(ret < 0);
|
|
|
|
|
|
|
|
for (i = 0; i < buf_info.nbuffers; i++)
|
|
|
|
{
|
|
|
|
if (direct == AUDIO_TYPE_OUTPUT)
|
|
|
|
ret = audio_test_readbuffer(state, bufs[i]);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
streaming = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = audio_test_enqueuebuffer(state, bufs[i], direct);
|
|
|
|
assert_false(ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (running)
|
|
|
|
{
|
|
|
|
ret = audio_test_start(state, direct);
|
|
|
|
assert_false(ret < 0);
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Start %s. \n", direct == AUDIO_TYPE_OUTPUT ?
|
|
|
|
"Playback" : "Capture");
|
|
|
|
|
|
|
|
while (running)
|
|
|
|
{
|
|
|
|
ret = mq_receive(state->mq, (FAR char *)&msg, sizeof(msg), &prio);
|
|
|
|
if (ret != sizeof(msg))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (msg.msg_id)
|
|
|
|
{
|
|
|
|
case AUDIO_MSG_DEQUEUE:
|
|
|
|
if (streaming)
|
|
|
|
{
|
|
|
|
if (direct == AUDIO_TYPE_INPUT)
|
|
|
|
{
|
|
|
|
ret = audio_test_writebuffer(state, msg.u.ptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = audio_test_readbuffer(state, msg.u.ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != OK)
|
|
|
|
{
|
|
|
|
streaming = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = audio_test_enqueuebuffer(state,
|
|
|
|
msg.u.ptr, direct);
|
|
|
|
if (ret != OK)
|
|
|
|
{
|
|
|
|
close(state->out_fd);
|
|
|
|
state->out_fd = -1;
|
|
|
|
streaming = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AUDIO_MSG_COMPLETE:
|
|
|
|
running = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Capture stopped */
|
|
|
|
|
|
|
|
if (audio_test_timeout(state, direct, start) || !streaming)
|
|
|
|
{
|
|
|
|
ret = audio_test_stop(state, direct);
|
|
|
|
assert_false(ret < 0);
|
|
|
|
running = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ret = mq_getattr(state->mq, &attr);
|
|
|
|
assert_false(ret < 0);
|
|
|
|
|
|
|
|
if (attr.mq_curmsgs == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mq_receive(state->mq, (FAR char *)&msg, sizeof(msg), &prio);
|
|
|
|
}
|
|
|
|
while (ret >= 0);
|
|
|
|
|
|
|
|
ret = audio_test_free_buffer(state, bufs, &buf_info, direct);
|
|
|
|
assert_false(ret < 0);
|
|
|
|
|
|
|
|
ret = audio_test_cleanup(state, direct);
|
|
|
|
assert_false(ret < 0);
|
|
|
|
|
|
|
|
memset(&buf_info, 0, sizeof(buf_info));
|
|
|
|
free(bufs);
|
|
|
|
bufs = NULL;
|
|
|
|
|
|
|
|
x++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int audio_test_setup(FAR void **audio_state)
|
|
|
|
{
|
|
|
|
FAR struct audio_state_s *state;
|
|
|
|
struct ap_buffer_info_s buf_info;
|
|
|
|
struct mq_attr attr;
|
|
|
|
int maxmsg = 0;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
state = (struct audio_state_s *)*audio_state;
|
|
|
|
|
|
|
|
if (state->direction & AUDIO_TYPE_OUTPUT)
|
|
|
|
{
|
|
|
|
state->outdev_fd = open(state->outdev, O_RDWR | O_CLOEXEC);
|
|
|
|
assert_false(state->outdev_fd < 0);
|
|
|
|
|
|
|
|
ret = ioctl(state->outdev_fd, AUDIOIOC_GETBUFFERINFO,
|
|
|
|
(unsigned long)&buf_info);
|
|
|
|
maxmsg = MAX(maxmsg,
|
|
|
|
ret >= 0 ? buf_info.nbuffers : CONFIG_AUDIO_NUM_BUFFERS);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->direction & AUDIO_TYPE_INPUT)
|
|
|
|
{
|
|
|
|
state->indev_fd = open(state->indev, O_RDWR | O_CLOEXEC);
|
|
|
|
assert_false(state->indev_fd < 0);
|
|
|
|
|
|
|
|
ret = ioctl(state->indev_fd, AUDIOIOC_GETBUFFERINFO,
|
|
|
|
(unsigned long)&buf_info);
|
|
|
|
maxmsg = MAX(maxmsg,
|
|
|
|
ret >= 0 ? buf_info.nbuffers : CONFIG_AUDIO_NUM_BUFFERS);
|
|
|
|
}
|
|
|
|
|
|
|
|
attr.mq_maxmsg = maxmsg + 8;
|
|
|
|
attr.mq_msgsize = sizeof(struct audio_msg_s);
|
|
|
|
attr.mq_curmsgs = 0;
|
|
|
|
attr.mq_flags = 0;
|
|
|
|
|
|
|
|
snprintf(state->mqname, sizeof(state->mqname), "/tmp/%0lx",
|
|
|
|
(unsigned long)((uintptr_t)state));
|
|
|
|
|
|
|
|
state->mq = mq_open(state->mqname, O_RDWR | O_CREAT, 0644, &attr);
|
|
|
|
assert_false(state->mq < 0);
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int audio_test_teardown(FAR void **audio_state)
|
|
|
|
{
|
|
|
|
FAR struct audio_state_s *state;
|
|
|
|
state = (struct audio_state_s *)*audio_state;
|
|
|
|
|
|
|
|
if (state->outdev_fd >= 0)
|
|
|
|
{
|
|
|
|
close(state->outdev_fd);
|
|
|
|
state->outdev_fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->outdev_fd >= 0)
|
|
|
|
{
|
|
|
|
close(state->indev_fd);
|
|
|
|
state->indev_fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mq_close(state->mq);
|
|
|
|
mq_unlink(state->mqname);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: loopback_test_main
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int main(int argc, FAR char *argv[])
|
|
|
|
{
|
|
|
|
struct audio_state_s state = {
|
|
|
|
.outdev = "/dev/audio/pcm0p",
|
|
|
|
.indev = "/dev/audio/pcm0c",
|
|
|
|
.duration = 10,
|
|
|
|
.chans = 2,
|
|
|
|
.bpsamp = 16,
|
|
|
|
.samprate = 44100,
|
|
|
|
.format = AUDIO_FMT_PCM,
|
|
|
|
.chmap = 0,
|
|
|
|
.outdev_fd = -1,
|
|
|
|
.indev_fd = -1,
|
|
|
|
.out_fd = -1,
|
|
|
|
};
|
|
|
|
|
|
|
|
parse_commandline(&state, argc, argv);
|
|
|
|
|
|
|
|
const struct CMUnitTest tests[] =
|
|
|
|
{
|
|
|
|
cmocka_unit_test_prestate_setup_teardown(audio_test_case,
|
|
|
|
audio_test_setup,
|
|
|
|
audio_test_teardown,
|
|
|
|
&state),
|
|
|
|
};
|
|
|
|
|
|
|
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
|
|
}
|