--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed 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.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * apps/examples/nxplayer/nxplayer.c
+ *
+ * Developed by:
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * With ongoing support:
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Greory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <tinyara/config.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dirent.h>
+#include <debug.h>
+
+#include <tinyara/audio/audio.h>
+#include <apps/nxplayer.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define NXPLAYER_STATE_IDLE 0
+#define NXPLAYER_STATE_PLAYING 1
+#define NXPLAYER_STATE_PAUSED 2
+
+#ifndef CONFIG_AUDIO_NUM_BUFFERS
+#define CONFIG_AUDIO_NUM_BUFFERS 2
+#endif
+
+#ifndef CONFIG_AUDIO_BUFFER_NUMBYTES
+#define CONFIG_AUDIO_BUFFER_NUMBYTES 8192
+#endif
+
+#ifndef CONFIG_NXPLAYER_MSG_PRIO
+#define CONFIG_NXPLAYER_MSG_PRIO 1
+#endif
+
+#ifndef CONFIG_NXPLAYER_PLAYTHREAD_STACKSIZE
+#define CONFIG_NXPLAYER_PLAYTHREAD_STACKSIZE 1500
+#endif
+
+/****************************************************************************
+ * Private Type Declarations
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_FMT_FROM_EXT
+struct nxplayer_ext_fmt_s {
+ const char *ext;
+ uint16_t format;
+ CODE int (*getsubformat)(FAR FILE *fd);
+};
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_FORMAT_MIDI
+int nxplayer_getmidisubformat(FAR FILE *fd);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_FMT_FROM_EXT
+static const struct nxplayer_ext_fmt_s g_known_ext[] = {
+#ifdef CONFIG_AUDIO_FORMAT_AC3
+ {"ac3", AUDIO_FMT_AC3, NULL},
+#endif
+#ifdef CONFIG_AUDIO_FORMAT_MP3
+ {"mp3", AUDIO_FMT_MP3, NULL},
+#endif
+#ifdef CONFIG_AUDIO_FORMAT_DTS
+ {"dts", AUDIO_FMT_DTS, NULL},
+#endif
+#ifdef CONFIG_AUDIO_FORMAT_WMA
+ {"wma", AUDIO_FMT_WMA, NULL},
+#endif
+#ifdef CONFIG_AUDIO_FORMAT_PCM
+ {"wav", AUDIO_FMT_PCM, NULL},
+#endif
+#ifdef CONFIG_AUDIO_FORMAT_MIDI
+ {"mid", AUDIO_FMT_MIDI, nxplayer_getmidisubformat},
+ {"midi", AUDIO_FMT_MIDI, nxplayer_getmidisubformat},
+#endif
+#ifdef CONFIG_AUDIO_FORMAT_OGG_VORBIS
+ {"ogg", AUDIO_FMT_OGG_VORBIS, NULL}
+#endif
+};
+
+static const int g_known_ext_count = sizeof(g_known_ext) / sizeof(struct nxplayer_ext_fmt_s);
+#endif /* CONFIG_NXPLAYER_FMT_FROM_EXT */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxplayer_opendevice
+ *
+ * nxplayer_opendevice() either searches the Audio system for a device
+ * that is compatible with the specified audio format and opens it, or
+ * tries to open the prefered device if specified and validates that
+ * it supports the requested format.
+ *
+ * Return:
+ * OK if compatible device opened (searched or preferred)
+ * -ENODEV if no compatible device opened.
+ * -ENOENT if preferred device couldn't be opened.
+ *
+ ****************************************************************************/
+
+static int nxplayer_opendevice(FAR struct nxplayer_s *pPlayer, int format, int subfmt)
+{
+ /* If we have a preferred device, then open it */
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE
+ if (pPlayer->prefdevice[0] != '\0') {
+ /* Use the saved prefformat to test if the requested
+ * format is specified by the device
+ */
+
+ if ((pPlayer->prefformat & (1 << (format - 1))) == 0 || (pPlayer->preftype & AUDIO_TYPE_OUTPUT) == 0) {
+ /* Format not supported by the device */
+
+ auddbg("ERROR: Format not supported by device: %d\n", format);
+ return -ENODEV;
+ }
+
+ /* Device supports the format. Open the device file. */
+
+ pPlayer->devFd = open(pPlayer->prefdevice, O_RDWR);
+ if (pPlayer->devFd == -1) {
+ int errcode = errno;
+ DEBUGASSERT(errcode > 0);
+
+ auddbg("ERROR: Failed to open %s: %d\n", -errcode);
+ UNUSED(errcode);
+ return -ENOENT;
+ }
+
+ return OK;
+ }
+#endif
+
+#if defined(CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE) && \
+ defined(CONFIG_NXPLAYER_INCLUDE_DEVICE_SEARCH)
+
+ else
+#endif
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_DEVICE_SEARCH
+ {
+ struct audio_caps_s caps;
+ FAR struct dirent *pDevice;
+ FAR DIR *dirp;
+ char path[64];
+ uint8_t supported = true;
+ uint8_t x;
+
+ /* Search for a device in the audio device directory */
+
+#ifdef CONFIG_AUDIO_CUSTOM_DEV_PATH
+#ifdef CONFIG_AUDIO_DEV_ROOT
+ dirp = opendir("/dev");
+#else
+ dirp = opendir(CONFIG_AUDIO_DEV_PATH);
+#endif /* CONFIG_AUDIO_DEV_ROOT */
+#else
+ dirp = opendir("/dev/audio");
+#endif /* CONFIG_AUDIO_CUSTOM_DEV_PATH */
+ if (dirp == NULL) {
+ int errcode = errno;
+ DEBUGASSERT(errcode > 0);
+
+ auddbg("ERROR: Failed to open /dev/audio: %d\n", -errcode);
+ UNUSED(errcode);
+ return -ENODEV;
+ }
+
+ while ((pDevice = readdir(dirp)) != NULL) {
+ /* We found the next device. Try to open it and
+ * get its audio capabilities.
+ */
+
+#ifdef CONFIG_AUDIO_CUSTOM_DEV_PATH
+#ifdef CONFIG_AUDIO_DEV_ROOT
+ snprintf(path, sizeof(path), "/dev/%s", pDevice->d_name);
+#else
+ snprintf(path, sizeof(path), CONFIG_AUDIO_DEV_PATH "/%s", pDevice->d_name);
+#endif /* CONFIG_AUDIO_DEV_ROOT */
+#else
+ snprintf(path, sizeof(path), "/dev/audio/%s", pDevice->d_name);
+#endif /* CONFIG_AUDIO_CUSTOM_DEV_PATH */
+
+ if ((pPlayer->devFd = open(path, O_RDWR)) != -1) {
+ /* We have the device file open. Now issue an AUDIO ioctls to
+ * get the capabilities
+ */
+
+ caps.ac_len = sizeof(caps);
+ caps.ac_type = AUDIO_TYPE_QUERY;
+ caps.ac_subtype = AUDIO_TYPE_QUERY;
+
+ if (ioctl(pPlayer->devFd, AUDIOIOC_GETCAPS, (unsigned long)&caps)
+ == caps.ac_len) {
+ /* Test if this device supports the format we want */
+
+ if (((caps.ac_format.hw & (1 << (format - 1))) != 0) && (caps.ac_controls.b[0] & AUDIO_TYPE_OUTPUT)) {
+ /* Do subformat detection */
+
+ if (subfmt != AUDIO_FMT_UNDEF) {
+ /* Prepare to get sub-formats for this main format */
+
+ caps.ac_subtype = format;
+ caps.ac_format.b[0] = 0;
+
+ while (ioctl(pPlayer->devFd, AUDIOIOC_GETCAPS, (unsigned long)&caps) == caps.ac_len) {
+ /* Check the next set of 4 controls to find the subformat */
+
+ for (x = 0; x < sizeof(caps.ac_controls); x++) {
+ if (caps.ac_controls.b[x] == subfmt) {
+ /* Sub format supported! */
+
+ break;
+ } else if (caps.ac_controls.b[x] == AUDIO_SUBFMT_END) {
+ /* Sub format not supported */
+
+ supported = false;
+ break;
+ }
+ }
+
+ /* If we reached the end of the subformat list, then
+ * break out of the loop.
+ */
+
+ if (x != sizeof(caps.ac_controls)) {
+ break;
+ }
+
+ /* Increment ac_format.b[0] to get next set of subformats */
+
+ caps.ac_format.b[0]++;
+ }
+ }
+
+ /* Test if subformat needed and detected */
+
+ if (supported) {
+ /* Yes, it supports this format. Use this device */
+
+ closedir(dirp);
+ return OK;
+ }
+ }
+ }
+
+ /* Not this device! */
+
+ close(pPlayer->devFd);
+ }
+ }
+
+ /* Close the directory */
+
+ closedir(dirp);
+ }
+#endif /* CONFIG_NXPLAYER_INCLUDE_DEVICE_SEARCH */
+
+ /* Device not found */
+
+ auddbg("ERROR: Device not found\n");
+ pPlayer->devFd = -1;
+ return -ENODEV;
+}
+
+/****************************************************************************
+ * Name: nxplayer_getmidisubformat
+ *
+ * nxplayer_getmidisubformat() reads the MIDI header and determins the
+ * MIDI format of the file.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_FORMAT_MIDI
+int nxplayer_getmidisubformat(FAR FILE *fd)
+{
+ char type[2];
+ int ret;
+
+ /* Seek to location 8 in the file (the format type) */
+
+ fseek(fd, 8, SEEK_SET);
+ fread(type, 1, 2, fd);
+
+ /* Set return value based on type */
+
+ switch (type[1]) {
+ case 0:
+ ret = AUDIO_SUBFMT_MIDI_0;
+ break;
+
+ case 1:
+ ret = AUDIO_SUBFMT_MIDI_1;
+ break;
+
+ case 2:
+ ret = AUDIO_SUBFMT_MIDI_2;
+ break;
+ }
+ fseek(fd, 0, SEEK_SET);
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_fmtfromextension
+ *
+ * nxplayer_fmtfromextension() tries to determine the file format based
+ * on the extension of the supplied filename.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_FMT_FROM_EXT
+static inline int nxplayer_fmtfromextension(FAR struct nxplayer_s *pPlayer,
+ FAR const char *pFilename,
+ FAR int *subfmt)
+{
+ const char *pExt;
+ uint8_t x;
+ uint8_t c;
+
+ /* Find the file extension, if any */
+
+ x = strlen(pFilename) - 1;
+ while (x > 0) {
+ /* Seach backward for the first '.' */
+
+ if (pFilename[x] == '.') {
+ /* First '.' found. Now compare with known extensions */
+
+ pExt = &pFilename[x + 1];
+ for (c = 0; c < g_known_ext_count; c++) {
+ /* Test for extension match */
+
+ if (strcasecmp(pExt, g_known_ext[c].ext) == 0) {
+ /* Test if we have a sub-format detection routine */
+
+ if (subfmt && g_known_ext[c].getsubformat) {
+ *subfmt = g_known_ext[c].getsubformat(pPlayer->fileFd);
+ }
+
+ /* Return the format for this extension */
+
+ return g_known_ext[c].format;
+ }
+ }
+ }
+
+ /* Stop if we find a '/' */
+
+ if (pFilename[x] == '/') {
+ break;
+ }
+
+ x--;
+ }
+
+ return AUDIO_FMT_UNDEF;
+}
+#endif /* CONFIG_NXPLAYER_FMT_FROM_EXT */
+
+/****************************************************************************
+ * Name: nxplayer_fmtfromheader
+ *
+ * nxplayer_fmtfromheader() tries to determine the file format by checking
+ * the file header for known file types.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_FMT_FROM_HEADER
+static int nxplayer_fmtfromheader(FAR struct nxplayer_s *pPlayer)
+{
+ return AUDIO_FMT_UNDEF;
+}
+#endif /* CONFIG_NXPLAYER_FMT_FROM_HEADER */
+
+/****************************************************************************
+ * Name: nxplayer_mediasearch
+ *
+ * nxplayer_mediasearch() searches the subdirectories in the mediadir
+ * for the specified media file. We borrow the caller's path stack
+ * variable (playfile) to conserve stack space.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NXPLAYER_MEDIA_SEARCH) && defined(CONFIG_NXPLAYER_INCLUDE_MEDIADIR)
+static int nxplayer_mediasearch(FAR struct nxplayer_s *pPlayer,
+ FAR const char *pFilename,
+ FAR const char *path, int pathmax)
+{
+ return -ENOENT;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_readbuffer
+ *
+ * Read the next block of data from the media file into the specified
+ * buffer.
+ *
+ ****************************************************************************/
+
+static int nxplayer_readbuffer(FAR struct nxplayer_s *pPlayer,
+ FAR struct ap_buffer_s* apb)
+{
+ /* Validate the file is still open. It will be closed automatically when
+ * we encounter the end of file (or, perhaps, a read error that we cannot
+ * handle.
+ */
+
+ if (pPlayer->fileFd == NULL) {
+ /* Return -ENODATA to indicate that there is nothing more to read from
+ * the file.
+ */
+
+ return -ENODATA;
+ }
+
+ /* Read data into the buffer. */
+
+ apb->nbytes = fread(&apb->samp, 1, apb->nmaxbytes, pPlayer->fileFd);
+ apb->curbyte = 0;
+ apb->flags = 0;
+
+ if (apb->nbytes < apb->nmaxbytes) {
+#ifdef CONFIG_DEBUG
+ int errcode = errno;
+ int readerror = ferror(pPlayer->fileFd);
+
+ audvdbg("Closing audio file, nbytes=%d readerr=%d\n", apb->nbytes, readerror);
+#endif
+
+ /* End of file or read error.. We are finished with this file in any
+ * event.
+ */
+
+ fclose(pPlayer->fileFd);
+ pPlayer->fileFd = NULL;
+
+ /* Set a flag to indicate that this is the final buffer in the stream */
+
+ apb->flags |= AUDIO_APB_FINAL;
+
+#ifdef CONFIG_DEBUG
+ /* Was this a file read error */
+
+ if (apb->nbytes == 0 && readerror) {
+ DEBUGASSERT(errcode > 0);
+ auddbg("ERROR: fread failed: %d\n", errcode);
+ }
+#endif
+ }
+
+ /* Return OK to indicate that the buffer should be passed through to the
+ * audio device. This does not necessarily indicate that data was read
+ * correctly.
+ */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxplayer_enqueuebuffer
+ *
+ * Description:
+ * Enqueue the audio buffer in the downstream device. Normally we are
+ * called with a buffer of data to be enqueued in the audio stream.
+ *
+ * Be we may also receive an empty length buffer (with only the
+ * AUDIO_APB_FINAL set) in the event of certin read error occurs or in the
+ * event that the file was an exact multiple of the nmaxbytes size of the
+ * audio buffer. In that latter case, we have an end of file with no bytes
+ * read.
+ *
+ * These infrequent zero length buffers have to be passed through because
+ * the include the AUDIO_APB_FINAL flag that is needed to terminate the
+ * audio stream.
+ *
+ ****************************************************************************/
+
+static int nxplayer_enqueuebuffer(FAR struct nxplayer_s *pPlayer,
+ FAR struct ap_buffer_s* apb)
+{
+ struct audio_buf_desc_s bufdesc;
+ int ret;
+
+ /* Now enqueue the buffer with the audio device. If the number of
+ * bytes in the file happens to be an exact multiple of the audio
+ * buffer size, then we will receive the last buffer size = 0. We
+ * encode this buffer also so the audio system knows its the end of
+ * the file and can do proper clean-up.
+ */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ bufdesc.session = pPlayer->session;
+#endif
+ bufdesc.numbytes = apb->nbytes;
+ bufdesc.u.pBuffer = apb;
+
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_ENQUEUEBUFFER, (unsigned long)&bufdesc);
+ if (ret < 0) {
+ int errcode = errno;
+ DEBUGASSERT(errcode > 0);
+
+ auddbg("ERROR: AUDIOIOC_ENQUEUEBUFFER ioctl failed: %d\n", errcode);
+ return -errcode;
+ }
+
+ /* Return OK to indicate that we successfully read data from the file
+ * (and we are not yet at the end of file)
+ */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxplayer_thread_playthread
+ *
+ * This is the thread that reads the audio file file and enqueues /
+ * dequeues buffers to the selected and opened audio device.
+ *
+ ****************************************************************************/
+
+static void *nxplayer_playthread(pthread_addr_t pvarg)
+{
+ struct nxplayer_s *pPlayer = (struct nxplayer_s *)pvarg;
+ struct audio_msg_s msg;
+ struct audio_buf_desc_s buf_desc;
+ ssize_t size;
+ bool running = true;
+ bool streaming = true;
+ bool failed = false;
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+ struct ap_buffer_info_s buf_info;
+ FAR struct ap_buffer_s **pBuffers;
+#else
+ FAR struct ap_buffer_s *pBuffers[CONFIG_AUDIO_NUM_BUFFERS];
+#endif
+#ifdef CONFIG_DEBUG
+ int outstanding = 0;
+#endif
+ int prio;
+ int x;
+ int ret;
+
+ audvdbg("Entry\n");
+
+ /* Query the audio device for it's preferred buffer size / qty */
+
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+ if ((ret = ioctl(pPlayer->devFd, AUDIOIOC_GETBUFFERINFO, (unsigned long)&buf_info)) != OK) {
+ /* Driver doesn't report it's buffer size. Use our default. */
+
+ buf_info.buffer_size = CONFIG_AUDIO_BUFFER_NUMBYTES;
+ buf_info.nbuffers = CONFIG_AUDIO_NUM_BUFFERS;
+ }
+
+ /* Create array of pointers to buffers */
+
+ pBuffers = (FAR struct ap_buffer_s **)malloc(buf_info.nbuffers * sizeof(FAR void *));
+ if (pBuffers == NULL) {
+ /* Error allocating memory for buffer storage! */
+
+ ret = -ENOMEM;
+ running = false;
+ goto err_out;
+ }
+
+ /* Create our audio pipeline buffers to use for queueing up data */
+
+ for (x = 0; x < buf_info.nbuffers; x++) {
+ pBuffers[x] = NULL;
+ }
+
+ for (x = 0; x < buf_info.nbuffers; x++)
+#else /* CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFER */
+
+ for (x = 0; x < CONFIG_AUDIO_NUM_BUFFERS; x++) {
+ pBuffers[x] = NULL;
+ }
+
+ for (x = 0; x < CONFIG_AUDIO_NUM_BUFFERS; x++)
+#endif /* CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFER */
+ {
+ /* Fill in the buffer descriptor struct to issue an alloc request */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ buf_desc.session = pPlayer->session;
+#endif
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+ buf_desc.numbytes = buf_info.buffer_size;
+#else
+ buf_desc.numbytes = CONFIG_AUDIO_BUFFER_NUMBYTES;
+#endif
+ buf_desc.u.ppBuffer = &pBuffers[x];
+
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_ALLOCBUFFER, (unsigned long)&buf_desc);
+ if (ret != sizeof(buf_desc)) {
+ /* Buffer alloc Operation not supported or error allocating! */
+
+ auddbg("ERROR: Could not allocate buffer %d\n", x);
+ running = false;
+ goto err_out;
+ }
+ }
+
+ /* Fill up the pipeline with enqueued buffers */
+
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+ for (x = 0; x < buf_info.nbuffers; x++)
+#else
+ for (x = 0; x < CONFIG_AUDIO_NUM_BUFFERS; x++)
+#endif
+ {
+ /* Read the next buffer of data */
+
+ ret = nxplayer_readbuffer(pPlayer, pBuffers[x]);
+ if (ret != OK) {
+ /* nxplayer_readbuffer will return an error if there is no further
+ * data to be read from the file. This can happen normally if the
+ * file is very small (less than will fit in
+ * CONFIG_AUDIO_NUM_BUFFERS) or if an error occurs trying to read
+ * from the file.
+ */
+
+ /* We are no longer streaming data from the file */
+
+ streaming = false;
+
+ if (x == 0) {
+ /* No buffers read? Should never really happen. Even in the
+ * case of a read failure, one empty buffer containing the
+ * AUDIO_APB_FINAL indication will be returned.
+ */
+
+ running = false;
+ }
+ }
+
+ /* Enqueue buffer by sending it to the audio driver */
+
+ else {
+ ret = nxplayer_enqueuebuffer(pPlayer, pBuffers[x]);
+ if (ret != OK) {
+ /* Failed to enqueue the buffer. The driver is not happy with
+ * the buffer. Perhaps a decoder has detected something that it
+ * does not like in the stream and has stopped streaming. This
+ * would happen normally if we send a file in the incorrect format
+ * to an audio decoder.
+ *
+ * We must stop streaming as gracefully as possible. Close the
+ * file so that no further data is read.
+ */
+
+ fclose(pPlayer->fileFd);
+ pPlayer->fileFd = NULL;
+
+ /* We are no longer streaming data from the file. Be we will
+ * need to wait for any outstanding buffers to be recovered. We
+ * also still expect the audio driver to send a AUDIO_MSG_COMPLETE
+ * message after all queued buffers have been returned.
+ */
+
+ streaming = false;
+ failed = true;
+ break;
+ }
+#ifdef CONFIG_DEBUG
+ else {
+ /* The audio driver has one more buffer */
+
+ outstanding++;
+ }
+#endif
+ }
+ }
+
+ audvdbg("%d buffers queued, running=%d streaming=%d\n", x, running, streaming);
+
+ /* Start the audio device */
+
+ if (running && !failed) {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_START, (unsigned long)pPlayer->session);
+#else
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_START, 0);
+#endif
+
+ if (ret < 0) {
+ /* Error starting the audio stream! We need to continue running
+ * in order to recover the audio buffers that have already been
+ * queued.
+ */
+
+ failed = true;
+ }
+ }
+
+ if (running && !failed) {
+ /* Indicate we are playing a file */
+
+ pPlayer->state = NXPLAYER_STATE_PLAYING;
+
+ /* Set initial parameters such as volume, bass, etc.
+ * REVISIT: Shouldn't this actually be done BEFORE we start playing?
+ */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+ (void)nxplayer_setvolume(pPlayer, pPlayer->volume);
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+ nxplayer_setbalance(pPlayer, pPlayer->balance);
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+ nxplayer_setbass(pPlayer, pPlayer->bass);
+ nxplayer_settreble(pPlayer, pPlayer->treble);
+#endif
+ }
+
+ /* Loop until we specifically break. running == true means that we are
+ * still looping waiting for the playback to complete. All of the file
+ * data may have been sent (if streaming == false), but the playback is
+ * not complete until we get the AUDIO_MSG_COMPLETE (or AUDIO_MSG_STOP)
+ * message
+ *
+ * The normal protocol for streaming errors detected by the audio driver
+ * is as follows:
+ *
+ * (1) The audio driver will indicated the error by returning a negated
+ * error value when the next buffer is enqueued. The upper level
+ * then knows that this buffer was not queue.
+ * (2) The audio driver must return all queued buffers using the
+ * AUDIO_MSG_DEQUEUE message, and
+ * (3) Terminate playing by sending the AUDIO_MSG_COMPLETE message.
+ */
+
+ audvdbg("%s\n", running ? "Playing..." : "Not runnning");
+ while (running) {
+ /* Wait for a signal either from the Audio driver that it needs
+ * additional buffer data, or from a user-space signal to pause,
+ * stop, etc.
+ */
+
+ size = mq_receive(pPlayer->mq, (FAR char *)&msg, sizeof(msg), &prio);
+
+ /* Validate a message was received */
+
+ if (size != sizeof(msg)) {
+ /* Interrupted by a signal? What to do? */
+
+ continue;
+ }
+
+ /* Perform operation based on message id */
+
+ switch (msg.msgId) {
+ /* An audio buffer is being dequeued by the driver */
+
+ case AUDIO_MSG_DEQUEUE:
+#ifdef CONFIG_DEBUG
+ /* Make sure that we believe that the audio driver has at
+ * least one buffer.
+ */
+
+ DEBUGASSERT(msg.u.pPtr && outstanding > 0);
+ outstanding--;
+#endif
+
+ /* Read data from the file directly into this buffer and
+ * re-enqueue it. streaming == true means that we have
+ * not yet hit the end-of-file.
+ */
+
+ if (streaming) {
+ /* Read the next buffer of data */
+
+ ret = nxplayer_readbuffer(pPlayer, msg.u.pPtr);
+ if (ret != OK) {
+ /* Out of data. Stay in the loop until the device sends
+ * us a COMPLETE message, but stop trying to play more
+ * data.
+ */
+
+ streaming = false;
+ }
+
+ /* Enqueue buffer by sending it to the audio driver */
+
+ else {
+ ret = nxplayer_enqueuebuffer(pPlayer, msg.u.pPtr);
+ if (ret != OK) {
+ /* There is some issue from the audio driver.
+ * Perhaps a problem in the file format?
+ *
+ * We must stop streaming as gracefully as possible.
+ * Close the file so that no further data is read.
+ */
+
+ fclose(pPlayer->fileFd);
+ pPlayer->fileFd = NULL;
+
+ /* Stop streaming and wait for buffers to be
+ * returned and to receive the AUDIO_MSG_COMPLETE
+ * indication.
+ */
+
+ streaming = false;
+ failed = true;
+ }
+#ifdef CONFIG_DEBUG
+ else {
+ /* The audio driver has one more buffer */
+
+ outstanding++;
+ }
+#endif
+ }
+ }
+ break;
+
+ /* Someone wants to stop the playback. */
+
+ case AUDIO_MSG_STOP:
+ /* Send a stop message to the device */
+
+ audvdbg("Stopping! outstanding=%d\n", outstanding);
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ioctl(pPlayer->devFd, AUDIOIOC_STOP, (unsigned long)pPlayer->session);
+#else
+ ioctl(pPlayer->devFd, AUDIOIOC_STOP, 0);
+#endif
+ /* Stay in the running loop (without sending more data).
+ * we will need to recover our audio buffers. We will
+ * loop until AUDIO_MSG_COMPLETE is received.
+ */
+
+ streaming = false;
+ break;
+
+ /* Message indicating the playback is complete */
+
+ case AUDIO_MSG_COMPLETE:
+ audvdbg("Play complete. outstanding=%d\n", outstanding);
+ DEBUGASSERT(outstanding == 0);
+ running = false;
+ break;
+
+ /* Unknown / unsupported message ID */
+
+ default:
+ break;
+ }
+ }
+
+ /* Release our audio buffers and unregister / release the device */
+
+err_out:
+ audvdbg("Clean-up and exit\n");
+
+ /* Unregister the message queue and release the session */
+
+ ioctl(pPlayer->devFd, AUDIOIOC_UNREGISTERMQ, (unsigned long)pPlayer->mq);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ioctl(pPlayer->devFd, AUDIOIOC_RELEASE, (unsigned long)pPlayer->session);
+#else
+ ioctl(pPlayer->devFd, AUDIOIOC_RELEASE, 0);
+#endif
+
+ /* Cleanup */
+
+ while (sem_wait(&pPlayer->sem) < 0) ;
+
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+ if (pBuffers != NULL) {
+ audvdbg("Freeing buffers\n");
+ for (x = 0; x < buf_info.nbuffers; x++) {
+ /* Fill in the buffer descriptor struct to issue a free request */
+
+ if (pBuffers[x] != NULL) {
+ buf_desc.u.pBuffer = pBuffers[x];
+ ioctl(pPlayer->devFd, AUDIOIOC_FREEBUFFER, (unsigned long)&buf_desc);
+ }
+ }
+
+ /* Free the pointers to the buffers */
+
+ free(pBuffers);
+ }
+#else
+ audvdbg("Freeing buffers\n");
+ for (x = 0; x < CONFIG_AUDIO_NUM_BUFFERS; x++) {
+ /* Fill in the buffer descriptor struct to issue a free request */
+
+ if (pBuffers[x] != NULL) {
+ buf_desc.u.pBuffer = pBuffers[x];
+ ioctl(pPlayer->devFd, AUDIOIOC_FREEBUFFER, (unsigned long)&buf_desc);
+ }
+ }
+#endif
+
+ /* Close the files */
+
+ if (pPlayer->fileFd != NULL) {
+ fclose(pPlayer->fileFd); /* Close the file */
+ pPlayer->fileFd = NULL; /* Clear out the FD */
+ }
+
+ close(pPlayer->devFd); /* Close the device */
+ pPlayer->devFd = -1; /* Mark device as closed */
+ mq_close(pPlayer->mq); /* Close the message queue */
+ mq_unlink(pPlayer->mqname); /* Unlink the message queue */
+ pPlayer->state = NXPLAYER_STATE_IDLE; /* Go to IDLE */
+
+ sem_post(&pPlayer->sem); /* Release the semaphore */
+
+ /* The playthread is done with the context. Release it, which may
+ * actually cause the context to be freed if the creator has already
+ * abandoned (released) the context too.
+ */
+
+ nxplayer_release(pPlayer);
+
+ audvdbg("Exit\n");
+
+ return NULL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxplayer_setvolume
+ *
+ * nxplayer_setvolume() sets the volume.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+int nxplayer_setvolume(FAR struct nxplayer_s *pPlayer, uint16_t volume)
+{
+ struct audio_caps_desc_s cap_desc;
+ int ret;
+
+ /* Thread sync using the semaphore */
+
+ while (sem_wait(&pPlayer->sem) < 0) ;
+
+ /* If we are currently playing, then we need to post a message to
+ * the playthread to perform the volume change operation. If we
+ * are not playing, then just store the volume setting and it will
+ * be applied before the next playback begins.
+ */
+
+ if (pPlayer->state == NXPLAYER_STATE_PLAYING) {
+ /* Send a CONFIGURE ioctl to the device to set the volume */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ cap_desc.session = pPlayer->session;
+#endif
+ 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] = volume;
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_CONFIGURE, (unsigned long)&cap_desc);
+ if (ret < 0) {
+ int errcode = errno;
+ DEBUGASSERT(errcode > 0);
+
+ auddbg("ERROR: AUDIOIOC_CONFIGURE ioctl failed: %d\n", errcode);
+ return -errcode;
+ }
+ }
+
+ /* Store the volume setting */
+
+ pPlayer->volume = volume;
+ sem_post(&pPlayer->sem);
+
+ return OK;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
+
+/****************************************************************************
+ * Name: nxplayer_setequalization
+ *
+ * Sets the level on each band of an equalizer. Each band setting is
+ * represented in one percent increments, so the range is 0-100.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * equalization - Pointer to array of equalizer settings of size
+ * CONFIG_AUDIO_EQUALIZER_NBANDS bytes. Each byte
+ * represents the setting for one band in the range of
+ * 0-100.
+ *
+ * Returned Value:
+ * OK if equalization was set correctly.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_EQUALIZER
+int nxplayer_setequalization(FAR struct nxplayer_s *pPlayer,
+ FAR uint8_t *equalization)
+{
+#warning Missing logic
+ return -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_setbass
+ *
+ * nxplayer_setbass() sets the bass level and range.
+ *
+ * Input:
+ * pPlayer - Pointer to the nxplayer context
+ * level - Bass level in percentage (0-100)
+ * range - Bass range in percentage (0-100)
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+int nxplayer_setbass(FAR struct nxplayer_s *pPlayer, uint8_t level)
+{
+ struct audio_caps_desc_s cap_desc;
+
+ /* Thread sync using the semaphore */
+
+ while (sem_wait(&pPlayer->sem) < 0) ;
+
+ /* If we are currently playing, then we need to post a message to
+ * the playthread to perform the volume change operation. If we
+ * are not playing, then just store the bass setting and it will
+ * be applied before the next playback begins.
+ */
+
+ if (pPlayer->state == NXPLAYER_STATE_PLAYING) {
+ /* Send a CONFIGURE ioctl to the device to set the volume */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ cap_desc.session = pPlayer->session;
+#endif
+ 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_BASS;
+ cap_desc.caps.ac_controls.b[0] = level;
+ ioctl(pPlayer->devFd, AUDIOIOC_CONFIGURE, (unsigned long)&cap_desc);
+ }
+
+ /* Store the volume setting */
+
+ pPlayer->bass = level;
+
+ sem_post(&pPlayer->sem);
+
+ return -ENOENT;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_TONE */
+
+/****************************************************************************
+ * Name: nxplayer_settreble
+ *
+ * nxplayer_settreble() sets the treble level and range.
+ *
+ * Input:
+ * pPlayer - Pointer to the nxplayer context
+ * level - Treble level in percentage (0-100)
+ * range - Treble range in percentage (0-100)
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+int nxplayer_settreble(FAR struct nxplayer_s *pPlayer, uint8_t level)
+{
+ struct audio_caps_desc_s cap_desc;
+
+ /* Thread sync using the semaphore */
+
+ while (sem_wait(&pPlayer->sem) < 0) ;
+
+ /* If we are currently playing, then we need to post a message to
+ * the playthread to perform the volume change operation. If we
+ * are not playing, then just store the treble setting and it will
+ * be applied before the next playback begins.
+ */
+
+ if (pPlayer->state == NXPLAYER_STATE_PLAYING) {
+ /* Send a CONFIGURE ioctl to the device to set the volume */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ cap_desc.session = pPlayer->session;
+#endif
+ 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_TREBLE;
+ cap_desc.caps.ac_controls.b[0] = level;
+ ioctl(pPlayer->devFd, AUDIOIOC_CONFIGURE, (unsigned long)&cap_desc);
+ }
+
+ /* Store the volume setting */
+
+ pPlayer->treble = level;
+
+ sem_post(&pPlayer->sem);
+
+ return -ENOENT;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_TONE */
+
+/****************************************************************************
+ * Name: nxplayer_setbalance
+ *
+ * nxplayer_setbalance() sets the volume.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+int nxplayer_setbalance(FAR struct nxplayer_s *pPlayer, uint16_t balance)
+{
+ struct audio_caps_desc_s cap_desc;
+
+ /* Thread sync using the semaphore */
+
+ while (sem_wait(&pPlayer->sem) < 0) ;
+
+ /* If we are currently playing, then we need to post a message to
+ * the playthread to perform the volume change operation. If we
+ * are not playing, then just store the volume setting and it will
+ * be applied before the next playback begins.
+ */
+
+ if (pPlayer->state == NXPLAYER_STATE_PLAYING) {
+ /* Send a CONFIGURE ioctl to the device to set the volume */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ cap_desc.session = pPlayer->session;
+#endif
+ 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_BALANCE;
+ cap_desc.caps.ac_controls.hw[0] = balance;
+ ioctl(pPlayer->devFd, AUDIOIOC_CONFIGURE, (unsigned long)&cap_desc);
+ }
+
+ /* Store the volume setting */
+
+ pPlayer->balance = balance;
+
+ sem_post(&pPlayer->sem);
+
+ return -ENOENT;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_pause
+ *
+ * nxplayer_pause() pauses playback without cancelling it.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+int nxplayer_pause(FAR struct nxplayer_s *pPlayer)
+{
+ int ret = OK;
+
+ if (pPlayer->state == NXPLAYER_STATE_PLAYING) {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_PAUSE, (unsigned long)pPlayer->session);
+#else
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_PAUSE, 0);
+#endif
+ if (ret == OK) {
+ pPlayer->state = NXPLAYER_STATE_PAUSED;
+ }
+ }
+
+ return ret;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
+
+/****************************************************************************
+ * Name: nxplayer_resume
+ *
+ * nxplayer_resume() resumes playback after a pause operation.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+int nxplayer_resume(FAR struct nxplayer_s *pPlayer)
+{
+ int ret = OK;
+
+ if (pPlayer->state == NXPLAYER_STATE_PAUSED) {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_RESUME, (unsigned long)pPlayer->session);
+#else
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_RESUME, 0);
+#endif
+ if (ret == OK) {
+ pPlayer->state = NXPLAYER_STATE_PLAYING;
+ }
+ }
+
+ return ret;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
+
+/****************************************************************************
+ * Name: nxplayer_fforward
+ *
+ * Selects to fast forward in the audio data stream. The fast forward
+ * operation can be cancelled by simply selected no sub-sampling with
+ * the AUDIO_SUBSAMPLE_NONE argument returning to normal 1x forward play.
+ * This function may be called multiple times to change fast forward rate.
+ *
+ * The preferred way to cancel a fast forward operation is via
+ * nxplayer_cancel_motion() that provides the option to also return to
+ * paused, non-playing state.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * subsample - Identifies the fast forward rate (in terms of sub-sampling,
+ * but does not explicitly require sub-sampling). See
+ * AUDIO_SUBSAMPLE_* definitions.
+ *
+ * Returned Value:
+ * OK if fast forward operation successful.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+int nxplayer_fforward(FAR struct nxplayer_s *pPlayer, uint8_t subsample)
+{
+ struct audio_caps_desc_s cap_desc;
+ int ret;
+
+ DEBUGASSERT(pPlayer && subsample >= AUDIO_SUBSAMPLE_NONE && subsample <= AUDIO_SUBSAMPLE_MAX);
+
+ /* Send a CONFIGURE ioctl to the device to set the forward rate */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ cap_desc.session = pPlayer->session;
+#endif
+ cap_desc.caps.ac_len = sizeof(struct audio_caps_s);
+ cap_desc.caps.ac_type = AUDIO_TYPE_PROCESSING;
+ cap_desc.caps.ac_format.hw = AUDIO_PU_SUBSAMPLE_FORWARD;
+ cap_desc.caps.ac_controls.b[0] = subsample;
+
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_CONFIGURE, (unsigned long)&cap_desc);
+ if (ret < 0) {
+ int errcode = errno;
+ DEBUGASSERT(errcode > 0);
+
+ auddbg("ERROR: ioctl AUDIOIOC_CONFIGURE failed: %d\n", errcode);
+ ret = -errcode;
+ }
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_rewind
+ *
+ * Selects to rewind in the audio data stream. The rewind operation must
+ * be cancelled with nxplayer_cancel_motion. This function may be called
+ * multiple times to change rewind rate.
+ *
+ * NOTE that cancellation of the rewind operation differs from
+ * cancellation of the fast forward operation because we must both restore
+ * the sub-sampling rate to 1x and also return to forward play.
+ * AUDIO_SUBSAMPLE_NONE is not a valid argument to this function.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * subsample - Identifies the rewind rate (in terms of sub-sampling, but
+ * does not explicitly require sub-sampling). See
+ * AUDIO_SUBSAMPLE_* definitions.
+ *
+ * Returned Value:
+ * OK if rewind operation successfully initiated.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_REWIND
+int nxplayer_rewind(FAR struct nxplayer_s *pPlayer, uint8_t subsample)
+{
+ struct audio_caps_desc_s cap_desc;
+ int ret;
+
+ DEBUGASSERT(pPlayer && subsample >= AUDIO_SUBSAMPLE_MIN && subsample <= AUDIO_SUBSAMPLE_MAX);
+
+ /* Send a CONFIGURE ioctl to the device to set the forward rate */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ cap_desc.session = pPlayer->session;
+#endif
+ cap_desc.caps.ac_len = sizeof(struct audio_caps_s);
+ cap_desc.caps.ac_type = AUDIO_TYPE_PROCESSING;
+ cap_desc.caps.ac_format.hw = AUDIO_PU_SUBSAMPLE_REWIND;
+ cap_desc.caps.ac_controls.b[0] = subsample;
+
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_CONFIGURE, (unsigned long)&cap_desc);
+ if (ret < 0) {
+ int errcode = errno;
+ DEBUGASSERT(errcode > 0);
+
+ auddbg("ERROR: ioctl AUDIOIOC_CONFIGURE failed: %d\n", errcode);
+ ret = -errcode;
+ }
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cancel_motion
+ *
+ * Cancel a rewind or fast forward operation and return to either the
+ * paused state or to the normal, forward play state.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * paused - True: return to the paused state, False: return to the 1X
+ * forward play state.
+ *
+ * Returned Value:
+ * OK if rewind operation successfully cancelled.
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_AUDIO_EXCLUDE_FFORWARD) || !defined(CONFIG_AUDIO_EXCLUDE_REWIND)
+int nxplayer_cancel_motion(FAR struct nxplayer_s *pPlayer, bool paused)
+{
+ int ret;
+
+ /* I think this is equivalent to calling nxplayer_fforward with the
+ * argument AUDIO_SUBSAMPLE_NONE: Forward motion with no sub-sampling.
+ *
+ * REVISIT: There is no way at present to cancel sub-sampling and return
+ * to pause atomically.
+ */
+
+ ret = nxplayer_fforward(pPlayer, AUDIO_SUBSAMPLE_NONE);
+ if (ret < 0) {
+ auddbg("ERROR: nxplayer_fforward failed: %d\n", ret);
+ return ret;
+ }
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+ if (paused) {
+ ret = nxplayer_pause(pPlayer);
+ if (ret < 0) {
+ auddbg("ERROR: nxplayer_pause failed: %d\n", ret);
+ return ret;
+ }
+ }
+#endif
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_setdevice
+ *
+ * nxplayer_setdevice() sets the perferred audio device to use with the
+ * provided nxplayer context.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE
+int nxplayer_setdevice(FAR struct nxplayer_s *pPlayer, FAR const char *pDevice)
+{
+ int tempFd;
+ struct audio_caps_s caps;
+
+ DEBUGASSERT(pPlayer != NULL);
+ DEBUGASSERT(pDevice != NULL);
+
+ /* Try to open the device */
+
+ tempFd = open(pDevice, O_RDWR);
+ if (tempFd == -1) {
+ /* Error opening the device */
+
+ return -ENOENT;
+ }
+
+ /* Validate it's an Audio device by issuing an AUDIOIOC_GETCAPS ioctl */
+
+ caps.ac_len = sizeof(caps);
+ caps.ac_type = AUDIO_TYPE_QUERY;
+ caps.ac_subtype = AUDIO_TYPE_QUERY;
+ if (ioctl(tempFd, AUDIOIOC_GETCAPS, (unsigned long)&caps) != caps.ac_len) {
+ /* Not an Audio device! */
+
+ close(tempFd);
+ return -ENODEV;
+ }
+
+ /* Close the file */
+
+ close(tempFd);
+
+ /* Save the path and format capabilities of the preferred device */
+
+ strncpy(pPlayer->prefdevice, pDevice, sizeof(pPlayer->prefdevice));
+ pPlayer->prefformat = caps.ac_format.b[0] | (caps.ac_format.b[1] << 8);
+ pPlayer->preftype = caps.ac_controls.b[0];
+
+ return OK;
+}
+#endif /* CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE */
+
+/****************************************************************************
+ * Name: nxplayer_stop
+ *
+ * nxplayer_stop() stops the current playback and closes the file and
+ * the associated device.
+ *
+ * Input:
+ * pPlayer Pointer to the initialized MPlayer context
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+int nxplayer_stop(FAR struct nxplayer_s *pPlayer)
+{
+ struct audio_msg_s term_msg;
+ FAR void *value;
+
+ DEBUGASSERT(pPlayer != NULL);
+
+ /* Validate we are not in IDLE state */
+
+ sem_wait(&pPlayer->sem); /* Get the semaphore */
+ if (pPlayer->state == NXPLAYER_STATE_IDLE) {
+ sem_post(&pPlayer->sem); /* Release the semaphore */
+ return OK;
+ }
+
+ sem_post(&pPlayer->sem);
+
+ /* Notify the playback thread that it needs to cancel the playback */
+
+ term_msg.msgId = AUDIO_MSG_STOP;
+ term_msg.u.data = 0;
+ mq_send(pPlayer->mq, (FAR const char *)&term_msg, sizeof(term_msg), CONFIG_NXPLAYER_MSG_PRIO);
+
+ /* Join the thread. The thread will do all the cleanup. */
+
+ pthread_join(pPlayer->playId, &value);
+ pPlayer->playId = 0;
+
+ return OK;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_STOP */
+
+/****************************************************************************
+ * Name: nxplayer_playfile
+ *
+ * nxplayer_playfile() tries to play the specified file using the Audio
+ * system. If a preferred device is specified, it will try to use that
+ * device otherwise it will perform a search of the Audio device files
+ * to find a suitable device.
+ *
+ * Input:
+ * pPlayer Pointer to the initialized MPlayer context
+ * pFilename Pointer to the filename to play
+ * filefmt Format of the file or AUD_FMT_UNDEF if unknown / to be
+ * determined by nxplayer_playfile()
+ *
+ * Returns:
+ * OK File is being played
+ * -EBUSY The media device is busy
+ * -ENOSYS The media file is an unsupported type
+ * -ENODEV No audio device suitable to play the media type
+ * -ENOENT The media file was not found
+ *
+ ****************************************************************************/
+
+int nxplayer_playfile(FAR struct nxplayer_s *pPlayer,
+ FAR const char *pFilename, int filefmt, int subfmt)
+{
+ struct mq_attr attr;
+ struct sched_param sparam;
+ pthread_attr_t tattr;
+ void *value;
+#ifdef CONFIG_NXPLAYER_INCLUDE_MEDIADIR
+ char path[128];
+#endif
+ int tmpsubfmt = AUDIO_FMT_UNDEF;
+ int ret;
+
+ DEBUGASSERT(pPlayer != NULL);
+ DEBUGASSERT(pFilename != NULL);
+
+ if (pPlayer->state != NXPLAYER_STATE_IDLE) {
+ return -EBUSY;
+ }
+
+ audvdbg("==============================\n");
+ audvdbg("Playing file %s\n", pFilename);
+ audvdbg("==============================\n");
+
+ /* Test that the specified file exists */
+
+ if ((pPlayer->fileFd = fopen(pFilename, "r")) == NULL) {
+ /* File not found. Test if its in the mediadir */
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_MEDIADIR
+ snprintf(path, sizeof(path), "%s/%s", pPlayer->mediadir, pFilename);
+
+ if ((pPlayer->fileFd = fopen(path, "r")) == NULL) {
+#ifdef CONFIG_NXPLAYER_MEDIA_SEARCH
+ /* File not found in the media dir. Do a search */
+
+ if (nxplayer_mediasearch(pPlayer, pFilename, path, sizeof(path)) != OK) {
+ auddbg("ERROR: Could not find file\n");
+ return -ENOENT;
+ }
+#else
+ auddbg("ERROR: Could not open %s or %s\n", pFilename, path);
+ return -ENOENT;
+#endif /* CONFIG_NXPLAYER_MEDIA_SEARCH */
+ }
+#else /* CONFIG_NXPLAYER_INCLUDE_MEDIADIR */
+
+ auddbg("ERROR: Could not open %s\n", pFilename);
+ return -ENOENT;
+#endif /* CONFIG_NXPLAYER_INCLUDE_MEDIADIR */
+ }
+#ifdef CONFIG_NXPLAYER_FMT_FROM_EXT
+ /* Try to determine the format of audio file based on the extension */
+
+ if (filefmt == AUDIO_FMT_UNDEF) {
+ filefmt = nxplayer_fmtfromextension(pPlayer, pFilename, &tmpsubfmt);
+ }
+#endif
+
+#ifdef CONFIG_NXPLAYER_FMT_FROM_HEADER
+ /* If type not identified, then test for known header types */
+
+ if (filefmt == AUDIO_FMT_UNDEF) {
+ filefmt = nxplayer_fmtfromheader(pPlayer, &subfmt, &tmpsubfmt);
+ }
+#endif
+
+ /* Test if we determined the file format */
+
+ if (filefmt == AUDIO_FMT_UNDEF) {
+ /* Hmmm, it's some unknown / unsupported type */
+
+ auddbg("ERROR: Unsupported format: %d \n", filefmt);
+ ret = -ENOSYS;
+ goto err_out_nodev;
+ }
+
+ /* Test if we have a sub format assignment from above */
+
+ if (subfmt == AUDIO_FMT_UNDEF) {
+ subfmt = tmpsubfmt;
+ }
+
+ /* Try to open the device */
+
+ ret = nxplayer_opendevice(pPlayer, filefmt, subfmt);
+ if (ret < 0) {
+ /* Error opening the device */
+
+ auddbg("ERROR: nxplayer_opendevice failed: %d\n", ret);
+ goto err_out_nodev;
+ }
+
+ /* Try to reserve the device */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_RESERVE, (unsigned long)&pPlayer->session);
+#else
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_RESERVE, 0);
+#endif
+ if (ret < 0) {
+ /* Device is busy or error */
+
+ auddbg("ERROR: Failed to reserve device: %d\n", ret);
+ ret = -errno;
+ goto err_out;
+ }
+
+ /* Create a message queue for the playthread */
+
+ attr.mq_maxmsg = 16;
+ attr.mq_msgsize = sizeof(struct audio_msg_s);
+ attr.mq_curmsgs = 0;
+ attr.mq_flags = 0;
+
+ snprintf(pPlayer->mqname, sizeof(pPlayer->mqname), "/tmp/%0lx", (unsigned long)((uintptr_t) pPlayer));
+
+ pPlayer->mq = mq_open(pPlayer->mqname, O_RDWR | O_CREAT, 0644, &attr);
+ if (pPlayer->mq == NULL) {
+ /* Unable to open message queue! */
+
+ ret = -errno;
+ auddbg("ERROR: mq_open failed: %d\n", ret);
+ goto err_out;
+ }
+
+ /* Register our message queue with the audio device */
+
+ ioctl(pPlayer->devFd, AUDIOIOC_REGISTERMQ, (unsigned long)pPlayer->mq);
+
+ /* Check if there was a previous thread and join it if there was
+ * to perform clean-up.
+ */
+
+ if (pPlayer->playId != 0) {
+ pthread_join(pPlayer->playId, &value);
+ }
+
+ /* Start the playfile thread to stream the media file to the
+ * audio device.
+ */
+
+ pthread_attr_init(&tattr);
+ sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 9;
+ (void)pthread_attr_setschedparam(&tattr, &sparam);
+ (void)pthread_attr_setstacksize(&tattr, CONFIG_NXPLAYER_PLAYTHREAD_STACKSIZE);
+
+ /* Add a reference count to the player for the thread and start the
+ * thread. We increment for the thread to avoid thread start-up
+ * race conditions.
+ */
+
+ nxplayer_reference(pPlayer);
+ ret = pthread_create(&pPlayer->playId, &tattr, nxplayer_playthread, (pthread_addr_t) pPlayer);
+ if (ret != OK) {
+ auddbg("ERROR: Failed to create playthread: %d\n", ret);
+ goto err_out;
+ }
+
+ /* Name the thread */
+
+ pthread_setname_np(pPlayer->playId, "playthread");
+ return OK;
+
+err_out:
+ close(pPlayer->devFd);
+ pPlayer->devFd = -1;
+
+err_out_nodev:
+ if (pPlayer->fileFd != NULL) {
+ fclose(pPlayer->fileFd);
+ pPlayer->fileFd = NULL;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxplayer_setmediadir
+ *
+ * nxplayer_setmediadir() sets the root path for media searches.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_MEDIADIR
+void nxplayer_setmediadir(FAR struct nxplayer_s *pPlayer,
+ FAR const char *mediadir)
+{
+ strncpy(pPlayer->mediadir, mediadir, sizeof(pPlayer->mediadir));
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_create
+ *
+ * nxplayer_create() allocates and initializes a nxplayer context for
+ * use by further nxplayer operations. This routine must be called before
+ * to perform the create for proper reference counting.
+ *
+ * Input Parameters: None
+ *
+ * Returned values:
+ * Pointer to the created context or NULL if there was an error.
+ *
+ ****************************************************************************/
+
+FAR struct nxplayer_s *nxplayer_create(void)
+{
+ FAR struct nxplayer_s *pPlayer;
+
+ /* Allocate the memory */
+
+ pPlayer = (FAR struct nxplayer_s *)malloc(sizeof(struct nxplayer_s));
+ if (pPlayer == NULL) {
+ return NULL;
+ }
+
+ /* Initialize the context data */
+
+ pPlayer->state = NXPLAYER_STATE_IDLE;
+ pPlayer->devFd = -1;
+ pPlayer->fileFd = NULL;
+#ifdef CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE
+ pPlayer->prefdevice[0] = '\0';
+ pPlayer->prefformat = 0;
+ pPlayer->preftype = 0;
+#endif
+ pPlayer->mq = NULL;
+ pPlayer->playId = 0;
+ pPlayer->crefs = 1;
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+ pPlayer->bass = 50;
+ pPlayer->treble = 50;
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+ pPlayer->balance = 500;
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+ pPlayer->volume = 400;
+#endif
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ pPlayer->session = NULL;
+#endif
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_MEDIADIR
+ strncpy(pPlayer->mediadir, CONFIG_NXPLAYER_DEFAULT_MEDIADIR, sizeof(pPlayer->mediadir));
+#endif
+ sem_init(&pPlayer->sem, 0, 1);
+
+ return pPlayer;
+}
+
+/****************************************************************************
+ * Name: nxplayer_release
+ *
+ * nxplayer_release() reduces the reference count by one and if it
+ * reaches zero, frees the context.
+ *
+ * Input Parameters:
+ * pPlayer Pointer to the NxPlayer context
+ *
+ * Returned values: None
+ *
+ ****************************************************************************/
+
+void nxplayer_release(FAR struct nxplayer_s *pPlayer)
+{
+ int refcount;
+ FAR void *value;
+
+ /* Grab the semaphore */
+
+ while (sem_wait(&pPlayer->sem) < 0) {
+ int errcode = errno;
+ DEBUGASSERT(errcode > 0);
+
+ if (errcode != EINTR) {
+ auddbg("ERROR: sem_wait failed: %d\n", errcode);
+ return;
+ }
+ }
+
+ /* Check if there was a previous thread and join it if there was */
+
+ if (pPlayer->playId != 0) {
+ sem_post(&pPlayer->sem);
+ pthread_join(pPlayer->playId, &value);
+ pPlayer->playId = 0;
+
+ while (sem_wait(&pPlayer->sem) < 0) {
+ int errcode = errno;
+ DEBUGASSERT(errcode > 0);
+
+ if (errcode != -EINTR) {
+ auddbg("ERROR: sem_wait failed: %d\n", errcode);
+ return;
+ }
+ }
+ }
+
+ /* Reduce the reference count */
+
+ refcount = pPlayer->crefs--;
+ sem_post(&pPlayer->sem);
+
+ /* If the ref count *was* one, then free the context */
+
+ if (refcount == 1) {
+ free(pPlayer);
+ }
+}
+
+/****************************************************************************
+ * Name: nxplayer_reference
+ *
+ * nxplayer_reference() increments the reference count by one.
+ *
+ * Input Parameters:
+ * pPlayer Pointer to the NxPlayer context
+ *
+ * Returned values: None
+ *
+ ****************************************************************************/
+
+void nxplayer_reference(FAR struct nxplayer_s *pPlayer)
+{
+ /* Grab the semaphore */
+
+ while (sem_wait(&pPlayer->sem) < 0) {
+ int errcode = errno;
+ DEBUGASSERT(errcode > 0);
+
+ if (errcode != -EINTR) {
+ auddbg("ERROR: sem_wait failed: %d\n", errcode);
+ return;
+ }
+ }
+
+ /* Increment the reference count */
+
+ pPlayer->crefs++;
+ sem_post(&pPlayer->sem);
+}
+
+/****************************************************************************
+ * Name: nxplayer_systemreset
+ *
+ * nxplayer_systemreset() performs a HW reset on all registered
+ * audio devices.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_SYSTEM_RESET
+int nxplayer_systemreset(FAR struct nxplayer_s *pPlayer)
+{
+ struct dirent *pDevice;
+ DIR *dirp;
+ char path[64];
+
+ /* Search for a device in the audio device directory */
+
+#ifdef CONFIG_AUDIO_CUSTOM_DEV_PATH
+#ifdef CONFIG_AUDIO_DEV_ROOT
+ dirp = opendir("/dev");
+#else
+ dirp = opendir(CONFIG_AUDIO_DEV_PATH);
+#endif
+#else
+ dirp = opendir("/dev/audio");
+#endif
+ if (dirp == NULL) {
+ return -ENODEV;
+ }
+
+ while ((pDevice = readdir(dirp)) != NULL) {
+ /* We found the next device. Try to open it and
+ * get its audio capabilities.
+ */
+
+#ifdef CONFIG_AUDIO_CUSTOM_DEV_PATH
+#ifdef CONFIG_AUDIO_DEV_ROOT
+ snprintf(path, sizeof(path), "/dev/%s", pDevice->d_name);
+#else
+ snprintf(path, sizeof(path), CONFIG_AUDIO_DEV_PATH "/%s", pDevice->d_name);
+#endif
+#else
+ snprintf(path, sizeof(path), "/dev/audio/%s", pDevice->d_name);
+#endif
+ if ((pPlayer->devFd = open(path, O_RDWR)) != -1) {
+ /* We have the device file open. Now issue an
+ * AUDIO ioctls to perform a HW reset
+ */
+
+ ioctl(pPlayer->devFd, AUDIOIOC_HWRESET, 0);
+
+ /* Now close the device */
+
+ close(pPlayer->devFd);
+ }
+
+ }
+
+ pPlayer->devFd = -1;
+ return OK;
+}
+#endif /* CONFIG_NXPLAYER_INCLUDE_SYSTEM_RESET */
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed 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.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * apps/examples/nxplayer/nxplayer_main.c
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <tinyara/config.h>
+#include <tinyara/audio/audio.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <apps/readline.h>
+#include <apps/nxplayer.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define NXPLAYER_VER "1.04"
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_HELP
+#define NXPLAYER_HELP_TEXT(x) #x
+#else
+#define NXPLAYER_HELP_TEXT(x)
+#endif
+
+/****************************************************************************
+ * Private Type Declarations
+ ****************************************************************************/
+
+struct mp_cmd_s {
+ const char *cmd; /* The command text */
+ const char *arghelp; /* Text describing the args */
+ nxplayer_func pFunc; /* Pointer to command handler */
+ const char *help; /* The help text */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int nxplayer_cmd_quit(FAR struct nxplayer_s *pPlayer, char *parg);
+static int nxplayer_cmd_play(FAR struct nxplayer_s *pPlayer, char *parg);
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_SYSTEM_RESET
+static int nxplayer_cmd_reset(FAR struct nxplayer_s *pPlayer, char *parg);
+#endif
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE
+static int nxplayer_cmd_device(FAR struct nxplayer_s *pPlayer, char *parg);
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+static int nxplayer_cmd_pause(FAR struct nxplayer_s *pPlayer, char *parg);
+static int nxplayer_cmd_resume(FAR struct nxplayer_s *pPlayer, char *parg);
+#endif
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_MEDIADIR
+static int nxplayer_cmd_mediadir(FAR struct nxplayer_s *pPlayer, char *parg);
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+static int nxplayer_cmd_stop(FAR struct nxplayer_s *pPlayer, char *parg);
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+static int nxplayer_cmd_volume(FAR struct nxplayer_s *pPlayer, char *parg);
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+static int nxplayer_cmd_balance(FAR struct nxplayer_s *pPlayer, char *parg);
+#endif
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+static int nxplayer_cmd_bass(FAR struct nxplayer_s *pPlayer, char *parg);
+static int nxplayer_cmd_treble(FAR struct nxplayer_s *pPlayer, char *parg);
+#endif
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_HELP
+static int nxplayer_cmd_help(FAR struct nxplayer_s *pPlayer, char *parg);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct mp_cmd_s g_nxplayer_cmds[] = {
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+ {"balance", "d%", nxplayer_cmd_balance, NXPLAYER_HELP_TEXT(Set balance percentage( < 50 % means more left))},
+#endif
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+ {"bass", "d%", nxplayer_cmd_bass, NXPLAYER_HELP_TEXT(Set bass level percentage)},
+#endif
+#ifdef CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE
+ {"device", "devfile", nxplayer_cmd_device, NXPLAYER_HELP_TEXT(Specify a preferred audio device)},
+#endif
+#ifdef CONFIG_NXPLAYER_INCLUDE_HELP
+ {"h", "", nxplayer_cmd_help, NXPLAYER_HELP_TEXT(Display help for commands)}
+ ,
+ {"help", "", nxplayer_cmd_help, NXPLAYER_HELP_TEXT(Display help for commands)}
+ ,
+#endif
+#ifdef CONFIG_NXPLAYER_INCLUDE_MEDIADIR
+ {"mediadir", "path", nxplayer_cmd_mediadir, NXPLAYER_HELP_TEXT(Change the media directory)}
+ ,
+#endif
+ {"play", "filename", nxplayer_cmd_play, NXPLAYER_HELP_TEXT(Play a media file)}
+ ,
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+ {"pause", "", nxplayer_cmd_pause, NXPLAYER_HELP_TEXT(Pause playback)}
+ ,
+#endif
+#ifdef CONFIG_NXPLAYER_INCLUDE_SYSTEM_RESET
+ {"reset", "", nxplayer_cmd_reset, NXPLAYER_HELP_TEXT(Perform a HW reset)}
+ ,
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+ {"resume", "", nxplayer_cmd_resume, NXPLAYER_HELP_TEXT(Resume playback)}
+ ,
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+ {"stop", "", nxplayer_cmd_stop, NXPLAYER_HELP_TEXT(Stop playback)}
+ ,
+#endif
+ {"tone", "freq secs", NULL, NXPLAYER_HELP_TEXT(Produce a pure tone)}
+ ,
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+ {"treble", "d%", nxplayer_cmd_treble, NXPLAYER_HELP_TEXT(Set treble level percentage)}
+ ,
+#endif
+ {"q", "", nxplayer_cmd_quit, NXPLAYER_HELP_TEXT(Exit NxPlayer)}
+ ,
+ {"quit", "", nxplayer_cmd_quit, NXPLAYER_HELP_TEXT(Exit NxPlayer)}
+ ,
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+ {"volume", "d%", nxplayer_cmd_volume, NXPLAYER_HELP_TEXT(Set volume to level specified)}
+#endif
+};
+static const int g_nxplayer_cmd_count = sizeof(g_nxplayer_cmds) / sizeof(struct mp_cmd_s);
+
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxplayer_cmd_play
+ *
+ * nxplayer_cmd_play() plays the specified media file using the nxplayer
+ * context.
+ *
+ ****************************************************************************/
+
+static int nxplayer_cmd_play(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ int ret;
+
+ /* Try to play the file specified */
+
+ ret = nxplayer_playfile(pPlayer, parg, AUDIO_FMT_UNDEF, AUDIO_FMT_UNDEF);
+
+ /* nxplayer_playfile returned values:
+ *
+ * OK File is being played
+ * -EBUSY The media device is busy
+ * -ENOSYS The media file is an unsupported type
+ * -ENODEV No audio device suitable to play the media type
+ * -ENOENT The media file was not found
+ */
+
+ switch (-ret) {
+ case OK:
+ break;
+
+ case ENODEV:
+ printf("No suitable Audio Device found\n");
+ break;
+
+ case EBUSY:
+ printf("Audio device busy\n");
+ break;
+
+ case ENOENT:
+ printf("File %s not found\n", parg);
+ break;
+
+ case ENOSYS:
+ printf("Unknown audio format\n");
+ break;
+
+ default:
+ printf("Error playing file: %d\n", -ret);
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxplayer_cmd_volume
+ *
+ * nxplayer_cmd_volume() sets the volume level.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+static int nxplayer_cmd_volume(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ uint16_t percent;
+
+ /* If no arg given, then print current volume */
+
+ if (parg == NULL || *parg == '\0') {
+ printf("volume: %d\n", pPlayer->volume / 10);
+ } else {
+ /* Get the percentage value from the argument */
+
+ percent = (uint16_t)(atof(parg) * 10.0);
+ nxplayer_setvolume(pPlayer, percent);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cmd_bass
+ *
+ * nxplayer_cmd_bass() sets the bass level and range.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+static int nxplayer_cmd_bass(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ uint8_t level_percent;
+
+ /* If no arg given, then print current bass */
+
+ if (parg == NULL || *parg == '\0') {
+ printf("bass: %d\n", pPlayer->bass);
+ } else {
+ /* Get the level and range percentage value from the argument */
+
+ level_percent = (uint8_t) atoi(parg);
+ nxplayer_setbass(pPlayer, level_percent);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cmd_treble
+ *
+ * nxplayer_cmd_treble() sets the treble level and range.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+static int nxplayer_cmd_treble(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ uint8_t level_percent;
+
+ /* If no arg given, then print current bass */
+
+ if (parg == NULL || *parg == '\0') {
+ printf("treble: %d\n", pPlayer->treble);
+ } else {
+ /* Get the level and range percentage value from the argument */
+
+ level_percent = (uint8_t) atoi(parg);
+ nxplayer_settreble(pPlayer, level_percent);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cmd_balance
+ *
+ * nxplayer_cmd_balance() sets the balance level.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+static int nxplayer_cmd_balance(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ uint16_t percent;
+
+ /* If no arg given, then print current volume */
+
+ if (parg == NULL || *parg == '\0') {
+ printf("balance: %d\n", pPlayer->volume / 10);
+ } else {
+ /* Get the percentage value from the argument */
+
+ percent = (uint16_t)(atof(parg) * 10.0);
+ nxplayer_setbalance(pPlayer, percent);
+ }
+
+ return OK;
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cmd_reset
+ *
+ * nxplayer_cmd_reset() performs a HW reset of all the audio devices.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_SYSTEM_RESET
+static int nxplayer_cmd_reset(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ nxplayer_systemreset(pPlayer);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cmd_mediadir
+ *
+ * nxplayer_cmd_mediadir() displays or changes the media directory
+ * context.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_MEDIADIR
+static int nxplayer_cmd_mediadir(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ /* If no arg given, then print current media dir */
+
+ if (parg == NULL || *parg == '\0') {
+ printf("%s\n", pPlayer->mediadir);
+ } else {
+ nxplayer_setmediadir(pPlayer, parg);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cmd_stop
+ *
+ * nxplayer_cmd_stop() stops playback of currently playing file
+ * context.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+static int nxplayer_cmd_stop(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ /* Stop the playback */
+
+ nxplayer_stop(pPlayer);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cmd_pause
+ *
+ * nxplayer_cmd_pause() pauses playback of currently playing file
+ * context.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+static int nxplayer_cmd_pause(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ /* Pause the playback */
+
+ nxplayer_pause(pPlayer);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cmd_resume
+ *
+ * nxplayer_cmd_resume() resumes playback of currently playing file
+ * context.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+static int nxplayer_cmd_resume(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ /* Resume the playback */
+
+ nxplayer_resume(pPlayer);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cmd_device
+ *
+ * nxplayer_cmd_device() sets the preferred audio device for playback
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE
+static int nxplayer_cmd_device(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ int ret;
+ char path[32];
+
+ /* First try to open the file directly */
+
+ ret = nxplayer_setdevice(pPlayer, parg);
+ if (ret == -ENOENT) {
+ /* Append the /dev/audio path and try again */
+
+#ifdef CONFIG_AUDIO_CUSTOM_DEV_PATH
+#ifdef CONFIG_AUDIO_DEV_ROOT
+ snprintf(path, sizeof(path), "/dev/%s", parg);
+#else
+ snprintf(path, sizeof(path), CONFIG_AUDIO_DEV_PATH "/%s", parg);
+#endif
+#else
+ snprintf(path, sizeof(path), "/dev/audio/%s", parg);
+#endif
+ ret = nxplayer_setdevice(pPlayer, path);
+ }
+
+ /* Test if the device file exists */
+
+ if (ret == -ENOENT) {
+ /* Device doesn't exit. Report error */
+
+ printf("Device %s not found\n", parg);
+ return ret;
+ }
+
+ /* Test if is is an audio device */
+
+ if (ret == -ENODEV) {
+ printf("Device %s is not an audio device\n", parg);
+ return ret;
+ }
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Device set successfully */
+
+ return OK;
+}
+#endif /* CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE */
+
+/****************************************************************************
+ * Name: nxplayer_cmd_quit
+ *
+ * nxplayer_cmd_quit() terminates the application
+ ****************************************************************************/
+
+static int nxplayer_cmd_quit(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ /* Stop the playback if any */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+ nxplayer_stop(pPlayer);
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxplayer_cmd_help
+ *
+ * nxplayer_cmd_help() displays the application's help information on
+ * supported commands and command syntax.
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_HELP
+static int nxplayer_cmd_help(FAR struct nxplayer_s *pPlayer, char *parg)
+{
+ int x, len, maxlen = 0;
+ int c;
+
+ /* Calculate length of longest cmd + arghelp */
+
+ for (x = 0; x < g_nxplayer_cmd_count; x++) {
+ len = strlen(g_nxplayer_cmds[x].cmd) + strlen(g_nxplayer_cmds[x].arghelp);
+ if (len > maxlen) {
+ maxlen = len;
+ }
+ }
+
+ printf("NxPlayer commands\n================\n");
+ for (x = 0; x < g_nxplayer_cmd_count; x++) {
+ /* Print the command and it's arguments */
+
+ printf(" %s %s", g_nxplayer_cmds[x].cmd, g_nxplayer_cmds[x].arghelp);
+
+ /* Calculate number of spaces to print before the help text */
+
+ len = maxlen - (strlen(g_nxplayer_cmds[x].cmd) + strlen(g_nxplayer_cmds[x].arghelp));
+ for (c = 0; c < len; c++) {
+ printf(" ");
+ }
+
+ printf(" : %s\n", g_nxplayer_cmds[x].help);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxplayer
+ *
+ * nxplayer() reads in commands from the console using the readline
+ * system add-in and implemets a command-line based media player that
+ * uses the NuttX audio system to play media files read in from the
+ * file system. Commands are provided for setting volume, base and
+ * other audio features, as well as for pausing and stoping the
+ * playback.
+ *
+ * Input Parameters:
+ * buf - The user allocated buffer to be filled.
+ * buflen - the size of the buffer.
+ * instream - The stream to read characters from
+ * outstream - The stream to each characters to.
+ *
+ * Returned values:
+ * On success, the (positive) number of bytes transferred is returned.
+ * EOF is returned to indicate either an end of file condition or a
+ * failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_BUILD_KERNEL
+int main(int argc, FAR char *argv[])
+#else
+int nxplayer_main(int argc, char *argv[])
+#endif
+{
+ char buffer[64];
+ int len, x, running;
+ char *cmd, *arg;
+ FAR struct nxplayer_s *pPlayer;
+
+ printf("NxPlayer version " NXPLAYER_VER "\n");
+ printf("h for commands, q to exit\n");
+ printf("\n");
+
+ /* Initialize our NxPlayer context */
+
+ pPlayer = nxplayer_create();
+ if (pPlayer == NULL) {
+ printf("Error: Out of RAM\n");
+ return -ENOMEM;
+ }
+
+ /* Loop until the user exits */
+
+ running = TRUE;
+ while (running) {
+ /* Print a prompt */
+
+ printf("nxplayer> ");
+ fflush(stdout);
+
+ /* Read a line from the terminal */
+
+ len = readline(buffer, sizeof(buffer), stdin, stdout);
+ buffer[len] = '\0';
+ if (len > 0) {
+ if (buffer[len - 1] == '\n') {
+ buffer[len - 1] = '\0';
+ }
+
+ /* Parse the command from the argument */
+
+ cmd = strtok_r(buffer, " \n", &arg);
+ if (cmd == NULL) {
+ continue;
+ }
+
+ /* Remove leading spaces from arg */
+
+ while (*arg == ' ') {
+ arg++;
+ }
+
+ /* Find the command in our cmd array */
+
+ for (x = 0; x < g_nxplayer_cmd_count; x++) {
+ if (strcmp(cmd, g_nxplayer_cmds[x].cmd) == 0) {
+ /* Command found. Call it's handler if not NULL */
+
+ if (g_nxplayer_cmds[x].pFunc != NULL) {
+ g_nxplayer_cmds[x].pFunc(pPlayer, arg);
+ }
+
+ /* Test if it is a quit command */
+
+ if (g_nxplayer_cmds[x].pFunc == nxplayer_cmd_quit) {
+ running = FALSE;
+ }
+ break;
+ }
+ }
+
+ /* Test for Unknown command */
+
+ if (x == g_nxplayer_cmd_count) {
+ printf("%s: unknown nxplayer command\n", buffer);
+ }
+ }
+ }
+
+ /* Release the NxPlayer context */
+
+ nxplayer_release(pPlayer);
+
+ return OK;
+}
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed 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.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * apps/include/nxplayer/nxplayer.h
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * With updates, enhancements, and modifications by:
+ *
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __APPS_SYSTEM_NXPLAYER_NXPLAYER_H
+#define __APPS_SYSTEM_NXPLAYER_NXPLAYER_H 1
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <tinyara/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+/* This structure describes the internal state of the NxPlayer */
+
+struct nxplayer_s {
+ int state; /* Current player state */
+ int devFd; /* File descriptor of active device */
+ mqd_t mq; /* Message queue for the playthread */
+ char mqname[16]; /* Name of our message queue */
+ pthread_t playId; /* Thread ID of the playthread */
+ int crefs; /* Number of references to the player */
+ sem_t sem; /* Thread sync semaphore */
+ FILE *fileFd; /* File descriptor of open file */
+#ifdef CONFIG_NXPLAYER_INCLUDE_PREFERRED_DEVICE
+ char prefdevice[CONFIG_NAME_MAX]; /* Preferred audio device */
+ int prefformat; /* Formats supported by preferred device */
+ int preftype; /* Types supported by preferred device */
+#endif
+#ifdef CONFIG_NXPLAYER_INCLUDE_MEDIADIR
+ char mediadir[CONFIG_NAME_MAX]; /* Root media directory where media is located */
+#endif
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ FAR void *session; /* Session assigment from device */
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+ uint16_t volume; /* Volume as a whole percentage (0-100) */
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+ uint16_t balance; /* Balance as a whole % (0=left off, 100=right off) */
+#endif
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+ uint16_t treble; /* Treble as a whole % */
+ uint16_t bass; /* Bass as a whole % */
+#endif
+};
+
+typedef int (*nxplayer_func)(FAR struct nxplayer_s *pPlayer, char *pargs);
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxplayer_create
+ *
+ * Allocates and Initializes a NxPlayer context that is passed to all
+ * nxplayer routines. The player MUST be destroyed using the
+ * nxplayer_destroy() routine since the context is reference counted.
+ * The context can be used in a mode where the caller creates the
+ * context, starts a file playing, and then forgets about the context
+ * and it will self free. This is because the nxplayer_playfile
+ * will also create a reference to the context, so the client calling
+ * nxplayer_destroy() won't actually de-allocate anything. The freeing
+ * will occur after the playthread has completed.
+ *
+ * Alternately, the caller can create the objec and hold on to it, then
+ * the context will persist until the original creator destroys it.
+ *
+ * Input Parameters: None
+ *
+ * Returned Value:
+ * Pointer to created NxPlayer context or NULL if error.
+ *
+ ****************************************************************************/
+
+FAR struct nxplayer_s *nxplayer_create(void);
+
+/****************************************************************************
+ * Name: nxplayer_release
+ *
+ * Reduces the reference count to the player and if it reaches zero,
+ * frees all memory used by the context.
+ *
+ * Input Parameters:
+ * pPlayer Pointer to the NxPlayer context
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void nxplayer_release(FAR struct nxplayer_s *pPlayer);
+
+/****************************************************************************
+ * Name: nxplayer_reference
+ *
+ * Increments the reference count to the player.
+ *
+ * Input Parameters:
+ * pPlayer Pointer to the NxPlayer context
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void nxplayer_reference(FAR struct nxplayer_s *pPlayer);
+
+/****************************************************************************
+ * Name: nxplayer_setdevice
+ *
+ * Sets the preferred Audio device to use with the instance of the
+ * nxplayer. Without a preferred device set, the nxplayer will search
+ * the audio subsystem to find a suitable device depending on the
+ * type of audio operation requested (i.e. an MP3 decoder device when
+ * playing an MP3 file, a WAV decoder device for a WAV file, etc.).
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * device - Pointer to pathname of the preferred device
+ *
+ * Returned Value:
+ * OK if context initialized successfully, error code otherwise.
+ *
+ ****************************************************************************/
+
+int nxplayer_setdevice(FAR struct nxplayer_s *pPlayer, FAR const char *device);
+
+/****************************************************************************
+ * Name: nxplayer_playfile
+ *
+ * Plays the specified media file (from the filesystem) using the
+ * Audio system. If a preferred device has been set, that device
+ * will be used for the playback, otherwise the first suitable device
+ * found in the /dev/audio directory will be used.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * filename - Pointer to pathname of the file to play
+ * filefmt - Format of audio in filename if known, AUDIO_FMT_UNDEF
+ * to let nxplayer_playfile() determine automatically.
+ * subfmt - Sub-Format of audio in filename if known, AUDIO_FMT_UNDEF
+ * to let nxplayer_playfile() determine automatically.
+ *
+ * Returned Value:
+ * OK if file found, device found, and playback started.
+ *
+ ****************************************************************************/
+
+int nxplayer_playfile(FAR struct nxplayer_s *pPlayer, FAR const char *filename, int filefmt, int subfmt);
+
+/****************************************************************************
+ * Name: nxplayer_stop
+ *
+ * Stops current playback.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ *
+ * Returned Value:
+ * OK if file found, device found, and playback started.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+int nxplayer_stop(FAR struct nxplayer_s *pPlayer);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_pause
+ *
+ * Pauses current playback.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ *
+ * Returned Value:
+ * OK if file found, device found, and playback started.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+int nxplayer_pause(FAR struct nxplayer_s *pPlayer);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_resume
+ *
+ * Resumes current playback.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ *
+ * Returned Value:
+ * OK if file found, device found, and playback started.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+int nxplayer_resume(FAR struct nxplayer_s *pPlayer);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_fforward
+ *
+ * Selects to fast forward in the audio data stream. The fast forward
+ * operation can be cancelled by simply selected no sub-sampling with
+ * the AUDIO_SUBSAMPLE_NONE argument returning to normal 1x forward play.
+ *
+ * The preferred way to cancel a fast forward operation is via
+ * nxplayer_cancel_motion() that provides the option to also return to
+ * paused, non-playing state.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * subsample - Identifies the fast forward rate (in terms of sub-sampling,
+ * but does not explicitly require sub-sampling). See
+ * AUDIO_SUBSAMPLE_* definitions.
+ *
+ * Returned Value:
+ * OK if fast forward operation successful.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+int nxplayer_fforward(FAR struct nxplayer_s *pPlayer, uint8_t subsample);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_rewind
+ *
+ * Selects to rewind in the audio data stream. The rewind operation must
+ * be cancelled with nxplayer_cancel_motion. This function may be called
+ * multiple times to change rewind rate.
+ *
+ * NOTE that cancellation of the rewind operation differs from
+ * cancellation of the fast forward operation because we must both restore
+ * the sub-sampling rate to 1x and also return to forward play.
+ * AUDIO_SUBSAMPLE_NONE is not a valid argument to this function.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * subsample - Identifies the rewind rate (in terms of sub-sampling, but
+ * does not explicitly require sub-sampling). See
+ * AUDIO_SUBSAMPLE_* definitions.
+ *
+ * Returned Value:
+ * OK if rewind operation successfully initiated.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_REWIND
+int nxplayer_rewind(FAR struct nxplayer_s *pPlayer, uint8_t subsample);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_cancel_motion
+ *
+ * Cancel a rewind or fast forward operation and return to either the
+ * paused state or to the normal, forward play state.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * paused - True: return to the paused state, False: return to the 1X
+ * forward play state.
+ *
+ * Returned Value:
+ * OK if rewind operation successfully cancelled.
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_AUDIO_EXCLUDE_FFORWARD) || !defined(CONFIG_AUDIO_EXCLUDE_REWIND)
+int nxplayer_cancel_motion(FAR struct nxplayer_s *pPlayer, bool paused);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_setvolume
+ *
+ * Sets the playback volume. The volume is represented in 1/10th of a
+ * percent increments, so the range is 0-1000. A value of 10 would mean
+ * 1%.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * volume - Volume level to set in 1/10th percent increments
+ *
+ * Returned Value:
+ * OK if file found, device found, and playback started.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+int nxplayer_setvolume(FAR struct nxplayer_s *pPlayer, uint16_t volume);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_setbalance
+ *
+ * Sets the playback balance. The balance is represented in 1/10th of a
+ * percent increments, so the range is 0-1000. A value of 10 would mean
+ * 1%.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * balance - Balance level to set in 1/10th percent increments
+ *
+ * Returned Value:
+ * OK if file found, device found, and playback started.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+int nxplayer_setbalance(FAR struct nxplayer_s *pPlayer, uint16_t balance);
+#endif
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_setmediadir
+ *
+ * Sets the root media directory for non-path qualified file searches.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * mediadir - Pointer to pathname of the media directory
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void nxplayer_setmediadir(FAR struct nxplayer_s *pPlayer, FAR const char *mediadir);
+
+/****************************************************************************
+ * Name: nxplayer_setequalization
+ *
+ * Sets the level on each band of an equalizer. Each band setting is
+ * represented in one percent increments, so the range is 0-100.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * equalization - Pointer to array of equalizer settings of size
+ * CONFIG_AUDIO_EQUALIZER_NBANDS bytes. Each byte
+ * represents the setting for one band in the range of
+ * 0-100.
+ *
+ * Returned Value:
+ * OK if equalization was set correctly.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_EQUALIZER
+int nxplayer_setequalization(FAR struct nxplayer_s *pPlayer, FAR uint8_t *equalization);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_setbass
+ *
+ * Sets the playback bass level. The bass is represented in one percent
+ * increments, so the range is 0-100.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * bass - Bass level to set in one percent increments
+ *
+ * Returned Value:
+ * OK if the bass level was set successfully
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+int nxplayer_setbass(FAR struct nxplayer_s *pPlayer, uint8_t bass);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_settreble
+ *
+ * Sets the playback treble level. The bass is represented in one percent
+ * increments, so the range is 0-100.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ * treble - Treble level to set in one percent increments
+ *
+ * Returned Value:
+ * OK if the treble level was set successfully
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+int nxplayer_settreble(FAR struct nxplayer_s *pPlayer, uint8_t treble);
+#endif
+
+/****************************************************************************
+ * Name: nxplayer_systemreset
+ *
+ * Performs an audio system reset, including a hardware reset on all
+ * registered audio devices.
+ *
+ * Input Parameters:
+ * pPlayer - Pointer to the context to initialize
+ *
+ * Returned Value:
+ * OK if file found, device found, and playback started.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NXPLAYER_INCLUDE_SYSTEM_RESET
+int nxplayer_systemreset(FAR struct nxplayer_s *pPlayer);
+#endif
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APPS_SYSTEM_NXPLAYER_NXPLAYER_H */