|
| 1 | +/**************************************************************************** |
| 2 | + * apps/audioutils/nxaudio/nxaudio.c |
| 3 | + * |
| 4 | + * Licensed to the Apache Software Foundation (ASF) under one or more |
| 5 | + * contributor license agreements. See the NOTICE file distributed with |
| 6 | + * this work for additional information regarding copyright ownership. The |
| 7 | + * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| 8 | + * "License"); you may not use this file except in compliance with the |
| 9 | + * License. You may obtain a copy of the License at |
| 10 | + * |
| 11 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 12 | + * |
| 13 | + * Unless required by applicable law or agreed to in writing, software |
| 14 | + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 15 | + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 16 | + * License for the specific language governing permissions and limitations |
| 17 | + * under the License. |
| 18 | + * |
| 19 | + ****************************************************************************/ |
| 20 | + |
| 21 | +/**************************************************************************** |
| 22 | + * Included Files |
| 23 | + ****************************************************************************/ |
| 24 | + |
| 25 | +#include <nuttx/config.h> |
| 26 | + |
| 27 | +#include <stdint.h> |
| 28 | +#include <stdio.h> |
| 29 | +#include <stdlib.h> |
| 30 | +#include <unistd.h> |
| 31 | +#include <fcntl.h> |
| 32 | +#include <mqueue.h> |
| 33 | +#include <sys/ioctl.h> |
| 34 | +#include <nuttx/audio/audio.h> |
| 35 | + |
| 36 | +#include <errno.h> |
| 37 | + |
| 38 | +#include <audioutils/nxaudio.h> |
| 39 | + |
| 40 | +/**************************************************************************** |
| 41 | + * Pre-processor Definitions |
| 42 | + ****************************************************************************/ |
| 43 | + |
| 44 | +/**************************************************************************** |
| 45 | + * Private Functions |
| 46 | + ****************************************************************************/ |
| 47 | + |
| 48 | +/**************************************************************************** |
| 49 | + * name: configure_audio |
| 50 | + ****************************************************************************/ |
| 51 | + |
| 52 | +static int configure_audio(int fd, int ch, int fs, int bps, int chmap) |
| 53 | +{ |
| 54 | + struct audio_caps_desc_s cap; |
| 55 | + |
| 56 | + cap.caps.ac_len = sizeof(struct audio_caps_s); |
| 57 | + cap.caps.ac_type = AUDIO_TYPE_OUTPUT; |
| 58 | + cap.caps.ac_channels = ch; |
| 59 | + cap.caps.ac_chmap = chmap; |
| 60 | + cap.caps.ac_controls.hw[0] = fs; |
| 61 | + cap.caps.ac_controls.b[2] = bps; |
| 62 | + cap.caps.ac_controls.b[3] = 0; /* Just set 0 */ |
| 63 | + |
| 64 | + return ioctl(fd, AUDIOIOC_CONFIGURE, (unsigned long)(uintptr_t)&cap); |
| 65 | +} |
| 66 | + |
| 67 | +/**************************************************************************** |
| 68 | + * name: create_audiomq |
| 69 | + ****************************************************************************/ |
| 70 | + |
| 71 | +static mqd_t create_audiomq(int fd, int buf_num) |
| 72 | +{ |
| 73 | + mqd_t ret; |
| 74 | + struct mq_attr attr; |
| 75 | + |
| 76 | + attr.mq_maxmsg = buf_num; |
| 77 | + attr.mq_msgsize = sizeof(struct audio_msg_s); |
| 78 | + attr.mq_curmsgs = 0; |
| 79 | + attr.mq_flags = 0; |
| 80 | + |
| 81 | + ret = mq_open(CONFIG_AUDIOUTILS_NXAUDIO_MSGQNAME, |
| 82 | + O_RDWR | O_CREAT, 0644, &attr); |
| 83 | + if (ret >= (mqd_t)0) |
| 84 | + { |
| 85 | + int rr; |
| 86 | + if ((rr = ioctl(fd, AUDIOIOC_REGISTERMQ, (unsigned long)ret)) < 0) |
| 87 | + { |
| 88 | + printf("mq register failed: %d, %d\n", rr, errno); |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + return ret; |
| 93 | +} |
| 94 | + |
| 95 | +/**************************************************************************** |
| 96 | + * name: create_audio_buffers |
| 97 | + ****************************************************************************/ |
| 98 | + |
| 99 | +static FAR struct ap_buffer_s **create_audio_buffers(int fd, int num, int sz) |
| 100 | +{ |
| 101 | + int i; |
| 102 | + struct audio_buf_desc_s desc; |
| 103 | + FAR struct ap_buffer_s **ret; |
| 104 | + |
| 105 | + ret = (FAR struct ap_buffer_s **)calloc(num, sizeof(FAR void *)); |
| 106 | + |
| 107 | + for (i = 0; i < num; i++) |
| 108 | + { |
| 109 | + desc.numbytes = sz; |
| 110 | + desc.u.pbuffer = &ret[i]; |
| 111 | + |
| 112 | + ioctl(fd, AUDIOIOC_ALLOCBUFFER, (unsigned long)(uintptr_t)&desc); |
| 113 | + } |
| 114 | + |
| 115 | + return ret; |
| 116 | +} |
| 117 | + |
| 118 | +/**************************************************************************** |
| 119 | + * name: free_audio_buffers |
| 120 | + ****************************************************************************/ |
| 121 | + |
| 122 | +static void free_audio_buffers(FAR struct nxaudio_s *nxaudio) |
| 123 | +{ |
| 124 | + int x; |
| 125 | + struct audio_buf_desc_s desc; |
| 126 | + |
| 127 | + for (x = 0; x < nxaudio->abufnum; x++) |
| 128 | + { |
| 129 | + if (nxaudio->abufs[x] != NULL) |
| 130 | + { |
| 131 | + desc.u.buffer = nxaudio->abufs[x]; |
| 132 | + ioctl(nxaudio->fd, AUDIOIOC_FREEBUFFER, |
| 133 | + (unsigned long)(uintptr_t)&desc); |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + free(nxaudio->abufs); |
| 138 | +} |
| 139 | + |
| 140 | +/**************************************************************************** |
| 141 | + * Public Functions |
| 142 | + ****************************************************************************/ |
| 143 | + |
| 144 | +/**************************************************************************** |
| 145 | + * name: fin_nxaudio |
| 146 | + ****************************************************************************/ |
| 147 | + |
| 148 | +void fin_nxaudio(FAR struct nxaudio_s *nxaudio) |
| 149 | +{ |
| 150 | + free_audio_buffers(nxaudio); |
| 151 | + ioctl(nxaudio->fd, AUDIOIOC_STOP, 0); |
| 152 | + ioctl(nxaudio->fd, AUDIOIOC_UNREGISTERMQ, (unsigned long)nxaudio->mq); |
| 153 | + ioctl(nxaudio->fd, AUDIOIOC_RELEASE, 0); |
| 154 | + ioctl(nxaudio->fd, AUDIOIOC_SHUTDOWN, 0); |
| 155 | + mq_close(nxaudio->mq); |
| 156 | + mq_unlink(CONFIG_AUDIOUTILS_NXAUDIO_MSGQNAME); |
| 157 | + close(nxaudio->fd); |
| 158 | +} |
| 159 | + |
| 160 | +/**************************************************************************** |
| 161 | + * name: init_nxaudio |
| 162 | + ****************************************************************************/ |
| 163 | + |
| 164 | +int init_nxaudio(FAR struct nxaudio_s *nxaudio, |
| 165 | + int fs, int bps, int chnum) |
| 166 | +{ |
| 167 | + struct ap_buffer_info_s buf_info; |
| 168 | + |
| 169 | + nxaudio->fd = open(CONFIG_AUDIOUTILS_NXAUDIO_DEVPATH, O_RDWR | O_CLOEXEC); |
| 170 | + if (nxaudio->fd >= 0) |
| 171 | + { |
| 172 | + if (ioctl(nxaudio->fd, AUDIOIOC_RESERVE, 0) < 0) |
| 173 | + { |
| 174 | + close(nxaudio->fd); |
| 175 | + return -1; |
| 176 | + } |
| 177 | + |
| 178 | + /* Audio configuration: set channel num, FS and bps */ |
| 179 | + |
| 180 | + configure_audio(nxaudio->fd, chnum, fs, bps, 0); |
| 181 | + |
| 182 | + nxaudio->chnum = chnum; |
| 183 | + |
| 184 | + ioctl(nxaudio->fd, AUDIOIOC_GETBUFFERINFO, |
| 185 | + (unsigned long)(uintptr_t)&buf_info); |
| 186 | + |
| 187 | + /* Create message queue to communicate with audio driver */ |
| 188 | + |
| 189 | + nxaudio->mq = create_audiomq(nxaudio->fd, buf_info.nbuffers + 8); |
| 190 | + |
| 191 | + /* Create audio buffers to inject audio sample */ |
| 192 | + |
| 193 | + nxaudio->abufs = create_audio_buffers(nxaudio->fd, |
| 194 | + buf_info.nbuffers, buf_info.buffer_size); |
| 195 | + nxaudio->abufnum = buf_info.nbuffers; |
| 196 | + |
| 197 | + return 0; |
| 198 | + } |
| 199 | + else |
| 200 | + { |
| 201 | + return -1; |
| 202 | + } |
| 203 | +} |
| 204 | + |
| 205 | +/**************************************************************************** |
| 206 | + * name: nxaudio_enqbuffer |
| 207 | + ****************************************************************************/ |
| 208 | + |
| 209 | +int nxaudio_enqbuffer(FAR struct nxaudio_s *nxaudio, |
| 210 | + FAR struct ap_buffer_s *apb) |
| 211 | +{ |
| 212 | + struct audio_buf_desc_s desc; |
| 213 | + |
| 214 | + desc.numbytes = apb->nbytes; |
| 215 | + desc.u.buffer = apb; |
| 216 | + |
| 217 | + return ioctl(nxaudio->fd, AUDIOIOC_ENQUEUEBUFFER, |
| 218 | + (unsigned long)(uintptr_t)&desc); |
| 219 | +} |
| 220 | + |
| 221 | +/**************************************************************************** |
| 222 | + * name: nxaudio_setvolume |
| 223 | + ****************************************************************************/ |
| 224 | + |
| 225 | +int nxaudio_setvolume(FAR struct nxaudio_s *nxaudio, uint16_t vol) |
| 226 | +{ |
| 227 | + struct audio_caps_desc_s cap_desc; |
| 228 | + |
| 229 | + cap_desc.caps.ac_len = sizeof(struct audio_caps_s); |
| 230 | + cap_desc.caps.ac_type = AUDIO_TYPE_FEATURE; |
| 231 | + cap_desc.caps.ac_format.hw = AUDIO_FU_VOLUME; |
| 232 | + cap_desc.caps.ac_controls.hw[0] = vol; |
| 233 | + |
| 234 | + return ioctl(nxaudio->fd, AUDIOIOC_CONFIGURE, |
| 235 | + (unsigned long)(uintptr_t)&cap_desc); |
| 236 | +} |
| 237 | + |
| 238 | +/**************************************************************************** |
| 239 | + * name: nxaudio_start |
| 240 | + ****************************************************************************/ |
| 241 | + |
| 242 | +int nxaudio_start(FAR struct nxaudio_s *nxaudio) |
| 243 | +{ |
| 244 | + return ioctl(nxaudio->fd, AUDIOIOC_START, 0); |
| 245 | +} |
| 246 | + |
| 247 | +/**************************************************************************** |
| 248 | + * name: nxaudio_start |
| 249 | + ****************************************************************************/ |
| 250 | + |
| 251 | +int nxaudio_stop(FAR struct nxaudio_s *nxaudio) |
| 252 | +{ |
| 253 | + struct audio_msg_s term_msg; |
| 254 | + |
| 255 | + term_msg.msg_id = AUDIO_MSG_STOP; |
| 256 | + term_msg.u.data = 0; |
| 257 | + mq_send(nxaudio->mq, (FAR const char *)&term_msg, sizeof(term_msg), 0); |
| 258 | + |
| 259 | + return OK; |
| 260 | +} |
| 261 | + |
| 262 | +/**************************************************************************** |
| 263 | + * name: nxaudio_msgloop |
| 264 | + ****************************************************************************/ |
| 265 | + |
| 266 | +int nxaudio_msgloop(FAR struct nxaudio_s *nxaudio, |
| 267 | + FAR struct nxaudio_callbacks_s *cbs, |
| 268 | + unsigned long arg) |
| 269 | +{ |
| 270 | + bool running = true; |
| 271 | + struct audio_msg_s msg; |
| 272 | + unsigned int prio; |
| 273 | + ssize_t size; |
| 274 | + |
| 275 | + if (!cbs) |
| 276 | + { |
| 277 | + return -1; |
| 278 | + } |
| 279 | + |
| 280 | + while (running) |
| 281 | + { |
| 282 | + size = mq_receive(nxaudio->mq, (FAR char *)&msg, sizeof(msg), &prio); |
| 283 | + if (size != sizeof(msg)) |
| 284 | + { |
| 285 | + continue; |
| 286 | + } |
| 287 | + |
| 288 | + switch (msg.msg_id) |
| 289 | + { |
| 290 | + case AUDIO_MSG_DEQUEUE: |
| 291 | + if (cbs->dequeue) |
| 292 | + { |
| 293 | + cbs->dequeue(arg, msg.u.ptr); |
| 294 | + } |
| 295 | + break; |
| 296 | + case AUDIO_MSG_COMPLETE: |
| 297 | + if (cbs->complete) |
| 298 | + { |
| 299 | + cbs->complete(arg); |
| 300 | + } |
| 301 | + break; |
| 302 | + |
| 303 | + case AUDIO_MSG_STOP: |
| 304 | + running = false; |
| 305 | + break; |
| 306 | + |
| 307 | + case AUDIO_MSG_USER: |
| 308 | + if (cbs->user) |
| 309 | + { |
| 310 | + cbs->user(arg, &msg, &running); |
| 311 | + } |
| 312 | + break; |
| 313 | + default: |
| 314 | + break; |
| 315 | + } |
| 316 | + } |
| 317 | + |
| 318 | + return 0; |
| 319 | +} |
0 commit comments