From 80eb94da8a189fde637a26b408112180b7bead77 Mon Sep 17 00:00:00 2001 From: Takayoshi Koizumi Date: Tue, 16 Aug 2022 08:50:17 +0000 Subject: [PATCH] audioutils/nxaudio: Add audio utility library for nuttx audio Add an utility library for easier use of NuttX's Audio driver. --- audioutils/nxaudio/Kconfig | 26 +++ audioutils/nxaudio/Make.defs | 23 +++ audioutils/nxaudio/Makefile | 25 +++ audioutils/nxaudio/nxaudio.c | 319 +++++++++++++++++++++++++++++++++++ include/audioutils/nxaudio.h | 79 +++++++++ 5 files changed, 472 insertions(+) create mode 100644 audioutils/nxaudio/Kconfig create mode 100644 audioutils/nxaudio/Make.defs create mode 100644 audioutils/nxaudio/Makefile create mode 100644 audioutils/nxaudio/nxaudio.c create mode 100644 include/audioutils/nxaudio.h diff --git a/audioutils/nxaudio/Kconfig b/audioutils/nxaudio/Kconfig new file mode 100644 index 000000000..e3828aff9 --- /dev/null +++ b/audioutils/nxaudio/Kconfig @@ -0,0 +1,26 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config AUDIOUTILS_NXAUDIO_LIB + bool "NX Audio Library" + default n + ---help--- + Enable support for the NX Audio library. + +if AUDIOUTILS_NXAUDIO_LIB + +config AUDIOUTILS_NXAUDIO_DEVPATH + string "Audio Device file path" + default "/dev/audio/pcm1" + ---help--- + Audio device file path of target audio device. + +config AUDIOUTILS_NXAUDIO_MSGQNAME + string "Message queue name" + default "/tmp/fmaudio_mq" + ---help--- + Message queue name (file path) to communicate with audio message loop. + +endif diff --git a/audioutils/nxaudio/Make.defs b/audioutils/nxaudio/Make.defs new file mode 100644 index 000000000..4a00aeee8 --- /dev/null +++ b/audioutils/nxaudio/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/audioutils/nxaudio/Make.defs +# +# 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. +# +############################################################################ + +ifeq ($(CONFIG_AUDIOUTILS_NXAUDIO_LIB),y) +CONFIGURED_APPS += $(APPDIR)/audioutils/nxaudio +endif diff --git a/audioutils/nxaudio/Makefile b/audioutils/nxaudio/Makefile new file mode 100644 index 000000000..482ee57ae --- /dev/null +++ b/audioutils/nxaudio/Makefile @@ -0,0 +1,25 @@ +############################################################################ +# apps/audioutils/nxaudio/Makefile +# +# 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +CSRCS = nxaudio.c + +include $(APPDIR)/Application.mk diff --git a/audioutils/nxaudio/nxaudio.c b/audioutils/nxaudio/nxaudio.c new file mode 100644 index 000000000..68d709a0b --- /dev/null +++ b/audioutils/nxaudio/nxaudio.c @@ -0,0 +1,319 @@ +/**************************************************************************** + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/**************************************************************************** + * 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; +} diff --git a/include/audioutils/nxaudio.h b/include/audioutils/nxaudio.h new file mode 100644 index 000000000..0d0acb133 --- /dev/null +++ b/include/audioutils/nxaudio.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * apps/include/audioutils/nxaudio.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_AUDIOUTILS_NXAUDIO_H +#define __APPS_INCLUDE_AUDIOUTILS_NXAUDIO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Public Data Types + ****************************************************************************/ + +struct nxaudio_s +{ + int fd; + + int abufnum; + FAR struct ap_buffer_s **abufs; + mqd_t mq; + + int chnum; +}; + +struct nxaudio_callbacks_s +{ + void CODE (*dequeue)(unsigned long arg, FAR struct ap_buffer_s *apb); + void CODE (*complete)(unsigned long arg); + void CODE (*user)(unsigned long arg, FAR struct audio_msg_s *msg, + FAR bool *running); +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +int init_nxaudio(FAR struct nxaudio_s *nxaudio, + int fs, int bps, int chnum); +void fin_nxaudio(FAR struct nxaudio_s *nxaudio); +int nxaudio_enqbuffer(FAR struct nxaudio_s *nxaudio, + FAR struct ap_buffer_s *apb); +int nxaudio_setvolume(FAR struct nxaudio_s *nxaudio, uint16_t vol); +int nxaudio_start(FAR struct nxaudio_s *nxaudio); +int nxaudio_msgloop(FAR struct nxaudio_s *nxaudio, + FAR struct nxaudio_callbacks_s *cbs, unsigned long arg); +int nxaudio_stop(FAR struct nxaudio_s *nxaudio); + +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_INCLUDE_AUDIOUTILS_NXAUDIO_H */