nuttx-apps/audioutils/nxaudio/nxaudio.c
Takayoshi Koizumi 80eb94da8a audioutils/nxaudio: Add audio utility library for nuttx audio
Add an utility  library for easier use of NuttX's Audio driver.
2022-08-25 20:29:55 +09:00

320 lines
9.1 KiB
C

/****************************************************************************
* apps/audioutils/nxaudio/nxaudio.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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <mqueue.h>
#include <sys/ioctl.h>
#include <nuttx/audio/audio.h>
#include <errno.h>
#include <audioutils/nxaudio.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* name: configure_audio
****************************************************************************/
static int configure_audio(int fd, int ch, int fs, int bps, int chmap)
{
struct audio_caps_desc_s cap;
cap.caps.ac_len = sizeof(struct audio_caps_s);
cap.caps.ac_type = AUDIO_TYPE_OUTPUT;
cap.caps.ac_channels = ch;
cap.caps.ac_chmap = chmap;
cap.caps.ac_controls.hw[0] = fs;
cap.caps.ac_controls.b[2] = bps;
cap.caps.ac_controls.b[3] = 0; /* Just set 0 */
return ioctl(fd, AUDIOIOC_CONFIGURE, (unsigned long)(uintptr_t)&cap);
}
/****************************************************************************
* name: create_audiomq
****************************************************************************/
static mqd_t create_audiomq(int fd, int buf_num)
{
mqd_t ret;
struct mq_attr attr;
attr.mq_maxmsg = buf_num;
attr.mq_msgsize = sizeof(struct audio_msg_s);
attr.mq_curmsgs = 0;
attr.mq_flags = 0;
ret = mq_open(CONFIG_AUDIOUTILS_NXAUDIO_MSGQNAME,
O_RDWR | O_CREAT, 0644, &attr);
if (ret >= (mqd_t)0)
{
int rr;
if ((rr = ioctl(fd, AUDIOIOC_REGISTERMQ, (unsigned long)ret)) < 0)
{
printf("mq register failed: %d, %d\n", rr, errno);
}
}
return ret;
}
/****************************************************************************
* name: create_audio_buffers
****************************************************************************/
static FAR struct ap_buffer_s **create_audio_buffers(int fd, int num, int sz)
{
int i;
struct audio_buf_desc_s desc;
FAR struct ap_buffer_s **ret;
ret = (FAR struct ap_buffer_s **)calloc(num, sizeof(FAR void *));
for (i = 0; i < num; i++)
{
desc.numbytes = sz;
desc.u.pbuffer = &ret[i];
ioctl(fd, AUDIOIOC_ALLOCBUFFER, (unsigned long)(uintptr_t)&desc);
}
return ret;
}
/****************************************************************************
* name: free_audio_buffers
****************************************************************************/
static void free_audio_buffers(FAR struct nxaudio_s *nxaudio)
{
int x;
struct audio_buf_desc_s desc;
for (x = 0; x < nxaudio->abufnum; x++)
{
if (nxaudio->abufs[x] != NULL)
{
desc.u.buffer = nxaudio->abufs[x];
ioctl(nxaudio->fd, AUDIOIOC_FREEBUFFER,
(unsigned long)(uintptr_t)&desc);
}
}
free(nxaudio->abufs);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* name: fin_nxaudio
****************************************************************************/
void fin_nxaudio(FAR struct nxaudio_s *nxaudio)
{
free_audio_buffers(nxaudio);
ioctl(nxaudio->fd, AUDIOIOC_STOP, 0);
ioctl(nxaudio->fd, AUDIOIOC_UNREGISTERMQ, (unsigned long)nxaudio->mq);
ioctl(nxaudio->fd, AUDIOIOC_RELEASE, 0);
ioctl(nxaudio->fd, AUDIOIOC_SHUTDOWN, 0);
mq_close(nxaudio->mq);
mq_unlink(CONFIG_AUDIOUTILS_NXAUDIO_MSGQNAME);
close(nxaudio->fd);
}
/****************************************************************************
* name: init_nxaudio
****************************************************************************/
int init_nxaudio(FAR struct nxaudio_s *nxaudio,
int fs, int bps, int chnum)
{
struct ap_buffer_info_s buf_info;
nxaudio->fd = open(CONFIG_AUDIOUTILS_NXAUDIO_DEVPATH, O_RDWR | O_CLOEXEC);
if (nxaudio->fd >= 0)
{
if (ioctl(nxaudio->fd, AUDIOIOC_RESERVE, 0) < 0)
{
close(nxaudio->fd);
return -1;
}
/* Audio configuration: set channel num, FS and bps */
configure_audio(nxaudio->fd, chnum, fs, bps, 0);
nxaudio->chnum = chnum;
ioctl(nxaudio->fd, AUDIOIOC_GETBUFFERINFO,
(unsigned long)(uintptr_t)&buf_info);
/* Create message queue to communicate with audio driver */
nxaudio->mq = create_audiomq(nxaudio->fd, buf_info.nbuffers + 8);
/* Create audio buffers to inject audio sample */
nxaudio->abufs = create_audio_buffers(nxaudio->fd,
buf_info.nbuffers, buf_info.buffer_size);
nxaudio->abufnum = buf_info.nbuffers;
return 0;
}
else
{
return -1;
}
}
/****************************************************************************
* name: nxaudio_enqbuffer
****************************************************************************/
int nxaudio_enqbuffer(FAR struct nxaudio_s *nxaudio,
FAR struct ap_buffer_s *apb)
{
struct audio_buf_desc_s desc;
desc.numbytes = apb->nbytes;
desc.u.buffer = apb;
return ioctl(nxaudio->fd, AUDIOIOC_ENQUEUEBUFFER,
(unsigned long)(uintptr_t)&desc);
}
/****************************************************************************
* name: nxaudio_setvolume
****************************************************************************/
int nxaudio_setvolume(FAR struct nxaudio_s *nxaudio, uint16_t vol)
{
struct audio_caps_desc_s cap_desc;
cap_desc.caps.ac_len = sizeof(struct audio_caps_s);
cap_desc.caps.ac_type = AUDIO_TYPE_FEATURE;
cap_desc.caps.ac_format.hw = AUDIO_FU_VOLUME;
cap_desc.caps.ac_controls.hw[0] = vol;
return ioctl(nxaudio->fd, AUDIOIOC_CONFIGURE,
(unsigned long)(uintptr_t)&cap_desc);
}
/****************************************************************************
* name: nxaudio_start
****************************************************************************/
int nxaudio_start(FAR struct nxaudio_s *nxaudio)
{
return ioctl(nxaudio->fd, AUDIOIOC_START, 0);
}
/****************************************************************************
* name: nxaudio_start
****************************************************************************/
int nxaudio_stop(FAR struct nxaudio_s *nxaudio)
{
struct audio_msg_s term_msg;
term_msg.msg_id = AUDIO_MSG_STOP;
term_msg.u.data = 0;
mq_send(nxaudio->mq, (FAR const char *)&term_msg, sizeof(term_msg), 0);
return OK;
}
/****************************************************************************
* name: nxaudio_msgloop
****************************************************************************/
int nxaudio_msgloop(FAR struct nxaudio_s *nxaudio,
FAR struct nxaudio_callbacks_s *cbs,
unsigned long arg)
{
bool running = true;
struct audio_msg_s msg;
unsigned int prio;
ssize_t size;
if (!cbs)
{
return -1;
}
while (running)
{
size = mq_receive(nxaudio->mq, (FAR char *)&msg, sizeof(msg), &prio);
if (size != sizeof(msg))
{
continue;
}
switch (msg.msg_id)
{
case AUDIO_MSG_DEQUEUE:
if (cbs->dequeue)
{
cbs->dequeue(arg, msg.u.ptr);
}
break;
case AUDIO_MSG_COMPLETE:
if (cbs->complete)
{
cbs->complete(arg);
}
break;
case AUDIO_MSG_STOP:
running = false;
break;
case AUDIO_MSG_USER:
if (cbs->user)
{
cbs->user(arg, &msg, &running);
}
break;
default:
break;
}
}
return 0;
}