diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-10-27 07:23:01 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-10-27 07:23:01 -0600 |
commit | 6dc974b4b0d8423cc9073e425a2e3a4370160df6 (patch) | |
tree | db597b33eff503c1eb0ea0dbf9f97c79af9bdbbb /apps/system/nxplayer | |
parent | 8acac95003c039ba20487ce04ec7cd6ca939bb53 (diff) | |
download | px4-nuttx-6dc974b4b0d8423cc9073e425a2e3a4370160df6.tar.gz px4-nuttx-6dc974b4b0d8423cc9073e425a2e3a4370160df6.tar.bz2 px4-nuttx-6dc974b4b0d8423cc9073e425a2e3a4370160df6.zip |
Add apps/system/nxplayer media player from Ken Pettit
Diffstat (limited to 'apps/system/nxplayer')
-rw-r--r-- | apps/system/nxplayer/.gitignore | 11 | ||||
-rw-r--r-- | apps/system/nxplayer/Kconfig | 109 | ||||
-rw-r--r-- | apps/system/nxplayer/Makefile | 131 | ||||
-rw-r--r-- | apps/system/nxplayer/README.txt | 17 | ||||
-rw-r--r-- | apps/system/nxplayer/nxplayer.c | 1513 | ||||
-rw-r--r-- | apps/system/nxplayer/nxplayer_main.c | 666 |
6 files changed, 2447 insertions, 0 deletions
diff --git a/apps/system/nxplayer/.gitignore b/apps/system/nxplayer/.gitignore new file mode 100644 index 000000000..83bd7b811 --- /dev/null +++ b/apps/system/nxplayer/.gitignore @@ -0,0 +1,11 @@ +/Make.dep +/.depend +/.built +/*.asm +/*.rel +/*.lst +/*.sym +/*.adb +/*.lib +/*.src +/*.obj diff --git a/apps/system/nxplayer/Kconfig b/apps/system/nxplayer/Kconfig new file mode 100644 index 000000000..6651422fc --- /dev/null +++ b/apps/system/nxplayer/Kconfig @@ -0,0 +1,109 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config SYSTEM_NXPLAYER + bool "NxPlayer library / command line support" + default n + ---help--- + Enable support for the command line media player + +if SYSTEM_NXPLAYER + +config NXPLAYER_COMMAND_LINE + bool "Include nxplayer command line application" + default y + ---help--- + Compiles in code for the nxplayer command line control. + This is a text-based command line interface that uses + the nxplayer library to play media files, control the + volume, balance, bass, etc. + +if NXPLAYER_COMMAND_LINE + +config NXPLAYER_INCLUDE_HELP + bool "Include HELP command and text" + default y + ---help--- + Compiles in the NxPlayer help text to provide online help + for available commands with syntax. + +endif + +config NXPLAYER_INCLUDE_DEVICE_SEARCH + bool "Include audio device search code" + default y + ---help--- + Compiles in extra code to search the audio device directory + for a suitable audio device to play the specified file. + Disabling this feature saves some code space, but it will + mean the calling application must specify the path of the + audio device to use before performing any other operations. + +config NXPLAYER_INCLUDE_PREFERRED_DEVICE + bool "Include preferred audio device specification code" + default y + ---help--- + Adds support for identifying a specific audio device to use + for audio operations. If this feature is not enabled, then + an audio device search will be performed. + +config NXPLAYER_FMT_FROM_EXT + bool "Include code to determine Audio format from extension" + default y + ---help--- + Compiles in extra code to determine audio format based + on the filename extension for known file types. + This feature is used if the format is not manually + specified, and will take priority over the more lengthy + file content detection approach. + +config NXPLAYER_FMT_FROM_HEADER + bool "Include code to find Audio format from file content" + default n + ---help--- + Compiles in extra code to determine audio format based + on the header content of a file for known file types. + This feature is used when the format type cannot be + determined from the filename extension. + +config NXPLAYER_INCLUDE_MEDIADIR + bool "Include support for specifying a media directory" + default y + ---help--- + Compiles in extra code to set a media directory which + will be searched when a request is made to play a file + which is not fully qualified. + +if NXPLAYER_INCLUDE_MEDIADIR + +config NXPLAYER_DEFAULT_MEDIADIR + string "Default root directory to search for media files" + default "/music" + ---help--- + Specifies a root directory to search for media files + when an absolute path is not provided. This can be + changed at the nxplayer command line, but will default + to this value each time nxplayer is launched. + +config NXPLAYER_RECURSIVE_MEDIA_SEARCH + bool "Perform recursive directory search for media files" + default n + ---help--- + When enabled, this feature will add code to perform + a complete recursive directory search within the + MEDIADIR for any media files that do not have a + qualified path (i.e. contain no '/' characters). + +endif + +config NXPLAYER_INCLUDE_SYSTEM_RESET + bool "Include support for system / hardware reset" + default n + ---help--- + When enabled, this feature will add code to enable issuing + a HW reset via program call. The system reset will perform + a reset on all registered audio devices. + +endif diff --git a/apps/system/nxplayer/Makefile b/apps/system/nxplayer/Makefile new file mode 100644 index 000000000..d2790e915 --- /dev/null +++ b/apps/system/nxplayer/Makefile @@ -0,0 +1,131 @@ +############################################################################ +# apps/system/nxplayer/Makefile +# +# Copyright (C) 2013 Ken Pettit. All rights reserved. +# Copyright (C) 2012-2013 Gregory Nutt. All rights reserved. +# Author: Ken Pettit <pettitkd@gmail.com> +# 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. +# +############################################################################ + +# TODO, this makefile should run make under the app dirs, instead of +# sourcing the Make.defs! + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +ifeq ($(WINTOOL),y) +INCDIROPT = -w +endif + +# NxPlayer Library + +ASRCS = +CSRCS = nxplayer.c + +# NxPlayer Application + +APPNAME = nxplayer +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = 3100 + +ifeq ($(CONFIG_NXPLAYER_COMMAND_LINE),y) +CSRCS += nxplayer_main.c +endif + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(CONFIG_WINDOWS_NATIVE),y) + BIN = ..\..\libapps$(LIBEXT) +else +ifeq ($(WINTOOL),y) + BIN = ..\\..\\libapps$(LIBEXT) +else + BIN = ../../libapps$(LIBEXT) +endif +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: context depend clean distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + $(call ARCHIVE, $(BIN), $(OBJS)) + $(Q) touch .built + +# Register application + +ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) +ifeq ($(CONFIG_NXPLAYER_COMMAND_LINE),y) +$(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat: $(DEPCONFIG) Makefile + $(call REGISTER,$(APPNAME),$(PRIORITY),$(STACKSIZE),$(APPNAME)_main) + +context: $(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat +else +context: +endif +else +context: +endif + +# Create dependencies + +.depend: Makefile $(SRCS) + $(Q) $(MKDEP) $(ROOTDEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep + $(Q) touch $@ + +depend: .depend + +clean: + $(call DELFILE, .built) + $(call CLEAN) + rm -rf ..$(DELIM)..$(DELIM)builtin$(DELIM)registry$(DELIM)$(APPNAME)_main.* + +distclean: clean + $(call DELFILE, Make.dep) + $(call DELFILE, .depend) + +-include Make.dep diff --git a/apps/system/nxplayer/README.txt b/apps/system/nxplayer/README.txt new file mode 100644 index 000000000..32d1f20fc --- /dev/null +++ b/apps/system/nxplayer/README.txt @@ -0,0 +1,17 @@ +NXPlayer +======== + + Source: NuttX + Author: Ken Pettit + Date: 11 Sept 2013 + +This application implements a command-line media player +which uses the NuttX Audio system to play files (mp3, +wav, etc.) from the file system. + +Usage: + nxplayer + +The application presents an command line for specifying +player commands, such as "play filename", "pause", +"volume 50%", etc. diff --git a/apps/system/nxplayer/nxplayer.c b/apps/system/nxplayer/nxplayer.c new file mode 100644 index 000000000..a86c5030c --- /dev/null +++ b/apps/system/nxplayer/nxplayer.c @@ -0,0 +1,1513 @@ +/**************************************************************************** + * apps/system/nxplayer/nxplayer.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 <nuttx/config.h> +#include <nuttx/audio/audio.h> +#include <debug.h> + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <dirent.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 + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +#ifdef CONFIG_NXPLAYER_FMT_FROM_EXT +struct nxplayer_ext_fmt_s +{ + const char *ext; + uint16_t format; +}; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * 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 }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_MP3 + { "mp3", AUDIO_FMT_MP3 }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_DTS + { "dts", AUDIO_FMT_DTS }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_WMA + { "wma", AUDIO_FMT_WMA }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_PCM + { "wav", AUDIO_FMT_PCM }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_MIDI + { "mid", AUDIO_FMT_MIDI }, + { "midi", AUDIO_FMT_MIDI }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_OGG_VORBIS + { "ogg", AUDIO_FMT_OGG_VORBIS } +#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) +{ + struct dirent* pDevice; + DIR* dirp; + char path[64]; + struct audio_caps_s caps; + + /* 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 & format) == 0) || + ((pPlayer->preftype & AUDIO_TYPE_OUTPUT) == 0)) + { + /* Format not supported by the device */ + + return -ENODEV; + } + + /* Device supports the format. Open the device file. */ + + pPlayer->devFd = open(pPlayer->prefdevice, O_RDWR); + if (pPlayer->devFd == -1) + 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 + { + /* 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) + { + 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 */ + + int ac_format = caps.ac_format[0] | (caps.ac_format[1] << 8); + if (((ac_format & format) != 0) && + (caps.ac_controls[0] & AUDIO_TYPE_OUTPUT)) + { + /* 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 */ + + pPlayer->devFd = -1; + return -ENODEV; +} + +/**************************************************************************** + * 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 int nxplayer_fmtfromextension(char* pFilename) +{ + const char *pExt; + int x, 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) + { + /* 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, char *pFilename, + char *path, int pathmax) +{ + return -ENOENT; +} +#endif + +/**************************************************************************** + * Name: nxplayer_enqueuebuffer + * + * Reads the next block of data from the media file into the specified + * buffer and enqueues it to the audio device. + * + ****************************************************************************/ + +static int nxplayer_enqueuebuffer(struct nxplayer_s *pPlayer, + struct ap_buffer_s* pBuf) +{ + struct audio_buf_desc_s bufdesc; + int ret; + + //auddbg("Entry: %p\n", pBuf); + + /* Validate the file is still open */ + + if (pPlayer->fileFd == NULL) + return OK; + + /* Read data into the buffer. */ + + pBuf->nbytes = fread(&pBuf->samp, 1, pBuf->nmaxbytes, pPlayer->fileFd); + if (pBuf->nbytes < pBuf->nmaxbytes) + { + fclose(pPlayer->fileFd); + pPlayer->fileFd = NULL; + } + + /* 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 cleanup. + */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION + bufdesc.session = pPlayer->session; +#endif + bufdesc.numbytes = pBuf->nbytes; + bufdesc.u.pBuffer = pBuf; + ret = ioctl(pPlayer->devFd, AUDIOIOC_ENQUEUEBUFFER, (unsigned long) + &bufdesc); + if (ret >= 0) + ret = OK; + else + ret = errno; + + return ret; +} + +/**************************************************************************** + * Name: nxplayer_thread_playthread + * + * This is the thread that reads the audio file file and enqueue's / + * 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; + int prio; + ssize_t size; + uint8_t running = TRUE; + uint8_t playing = TRUE; + int x, ret; +#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 + + auddbg("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("nxplayer_playthread: can't alloc 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 + { + /* Enqueue next buffer */ + + ret = nxplayer_enqueuebuffer(pPlayer, pBuffers[x]); + if (ret != OK) + { + /* Error encoding initial buffers or file is small */ + + if (x == 0) + running = FALSE; + else + playing = FALSE; + + break; + } + } + + /* Start the audio device */ + +#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! */ + + running = FALSE; + } + + /* Indicate we are playing a file */ + + pPlayer->state = NXPLAYER_STATE_PLAYING; + + /* Set parameters such as volume, bass, etc. */ + +#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME + 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 */ + + 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, &msg, sizeof(msg), &prio); + + /* Validate a message was received */ + + if (size != sizeof(msg)) + { + /* Interrupted by a signal? What to do? */ + + } + + /* Perform operation based on message id */ + + switch (msg.msgId) + { + /* An audio buffer is being dequeued by the driver */ + + case AUDIO_MSG_DEQUEUE: + + /* Read data from the file directly into this buffer + * and re-enqueue it. + */ + + if (playing) + { + ret = nxplayer_enqueuebuffer(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. + */ + + playing = FALSE; + } + } + break; + + /* Someone wants to stop the playback. */ + + case AUDIO_MSG_STOP: + + /* Send a stop message to the device */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION + ioctl(pPlayer->devFd, AUDIOIOC_STOP, + (unsigned long) pPlayer->session); +#else + ioctl(pPlayer->devFd, AUDIOIOC_STOP, 0); +#endif + playing = FALSE; + running = FALSE; + break; + + /* Message indicating the playback is complete */ + + case AUDIO_MSG_COMPLETE: + + running = FALSE; + break; + + /* Unknown / unsupported message ID */ + + default: + break; + } + } + + /* Release our audio buffers and unregister / release the device */ + +err_out: + /* 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) != OK) + ; + +#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS + if (pBuffers != NULL) + { + auddbg("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 + auddbg("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); + + auddbg("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; + + /* Thread sync using the semaphore */ + + while (sem_wait(&pPlayer->sem) != OK) + ; + + /* 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; + *((uint16_t *) cap_desc.caps.ac_format) = AUDIO_FU_VOLUME; + *((uint16_t *) cap_desc.caps.ac_controls) = volume; + ioctl(pPlayer->devFd, AUDIOIOC_CONFIGURE, (unsigned long) &cap_desc); + } + + /* Store the volume setting */ + + pPlayer->volume = volume; + + sem_post(&pPlayer->sem); + + return -ENOENT; +} +#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */ + +/**************************************************************************** + * 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) != OK) + ; + + /* 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; + *((uint16_t *) cap_desc.caps.ac_format) = AUDIO_FU_BASS; + cap_desc.caps.ac_controls[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) != OK) + ; + + /* 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; + *((uint16_t *) cap_desc.caps.ac_format) = AUDIO_FU_TREBLE; + cap_desc.caps.ac_controls[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) != OK) + ; + + /* 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; + *((uint16_t *) cap_desc.caps.ac_format) = AUDIO_FU_BALANCE; + *((uint16_t *) cap_desc.caps.ac_controls) = 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_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, 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[0] | (caps.ac_format[1] << 8); + pPlayer->preftype = caps.ac_controls[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, &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, char* pFilename, int filefmt) +{ + int ret; + struct mq_attr attr; + struct sched_param sparam; + pthread_attr_t tattr; + void* value; +#ifdef CONFIG_NXPLAYER_INCLUDE_MEDIADIR + char path[128]; +#endif + + DEBUGASSERT(pPlayer != NULL); + DEBUGASSERT(pFilename != NULL); + + if (pPlayer->state != NXPLAYER_STATE_IDLE) + { + return -EBUSY; + } + + auddbg("==============================\n"); + auddbg("Playing file %s\n", pFilename); + auddbg("==============================\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) + return -ENOENT; +#else + return -ENOENT; +#endif /* CONFIG_NXPLAYER_MEDIA_SEARCH */ + } + +#else /* CONFIG_NXPLAYER_INCLUDE_MEDIADIR */ + return -ENOENT; +#endif /* CONFIG_NXPLAYER_INCLUDE_MEDIADIR */ + } + + /* Try to determine the format of audio file based on the extension */ + +#ifdef CONFIG_NXPLAYER_FMT_FROM_EXT + if (filefmt == AUDIO_FMT_UNDEF) + filefmt = nxplayer_fmtfromextension(pFilename); +#endif + + /* If type not identified, then test for known header types */ + +#ifdef CONFIG_NXPLAYER_FMT_FROM_HEADER + if (filefmt == AUDIO_FMT_UNDEF) + filefmt = nxplayer_fmtfromheader(pPlayer); +#endif + + /* Test if we determined the file format */ + + if (filefmt == AUDIO_FMT_UNDEF) + { + /* Hmmm, it's some unknown / unsupported type */ + + ret = -ENOSYS; + goto err_out_nodev; + } + + /* Try to open the device */ + + ret = nxplayer_opendevice(pPlayer, filefmt); + if (ret < 0) + { + /* Error opening the device */ + + 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 */ + + 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/%0X", pPlayer); + pPlayer->mq = mq_open(pPlayer->mqname, O_RDWR | O_CREAT, 0644, &attr); + if (pPlayer->mq == NULL) + { + /* Unable to open message queue! */ + + ret = -errno; + 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 cleanup. + */ + + 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; + pthread_attr_setschedparam(&tattr, &sparam); + + /* 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 %d creating playthread\n", ret); + goto err_out; + } + + 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, 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, ret; + FAR void* value; + + /* Grab the semaphore */ + + while ((ret = sem_wait(&pPlayer->sem)) != OK) + { + if (ret != -EINTR) + { + auddbg("Error getting semaphore\n"); + 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 ((ret = sem_wait(&pPlayer->sem)) != OK) + { + if (ret != -EINTR) + { + auddbg("Error getting semaphore\n"); + 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) +{ + int ret; + + /* Grab the semaphore */ + + while ((ret = sem_wait(&pPlayer->sem)) != OK) + { + if (ret != -EINTR) + { + auddbg("Error getting semaphore\n"); + return; + } + } + + /* Increment the reference count */ + + pPlayer->crefs++; + sem_post(&pPlayer->sem); +} + +/**************************************************************************** + * Name: nxplayer_detach + * + * nxplayer_detach() detaches from the playthread to make it independant + * so the caller can abandon the context while the file is still + * being played. + * + * Input Parameters: + * pPlayer Pointer to the NxPlayer context + * + * Returned values: None + * + **************************************************************************/ + +void nxplayer_detach(FAR struct nxplayer_s* pPlayer) +{ +#if 0 + int ret; + + /* Grab the semaphore */ + + while ((ret = sem_wait(&pPlayer->sem)) != OK) + { + if (ret != -EINTR) + { + auddbg("Error getting semaphore\n"); + return; + } + } + + if (pPlayer->playId != NULL) + { + /* Do a pthread detach */ + + pthread_detach(pPlayer->playId); + pPlayer->playId = NULL; + } + + sem_post(&pPlayer->sem); +#endif +} + +/**************************************************************************** + * 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 */ + diff --git a/apps/system/nxplayer/nxplayer_main.c b/apps/system/nxplayer/nxplayer_main.c new file mode 100644 index 000000000..171684042 --- /dev/null +++ b/apps/system/nxplayer/nxplayer_main.c @@ -0,0 +1,666 @@ +/**************************************************************************** + * apps/system/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 <nuttx/config.h> +#include <nuttx/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); + + /* Test if the device file exists */ + + if (ret == -ENODEV) + { + printf("No suitable Audio Device found\n"); + } + + else if (ret == -EBUSY) + { + printf("Audio device busy\n"); + } + + else if (ret == -ENOENT) + { + printf("File %s not found\n", parg); + } + + else if (ret == -ENOSYS) + { + printf("Unknown audio format\n"); + } + + if (ret < 0) + { + return ret; + } + + /* File playing successfully */ + + return OK; +} + +/**************************************************************************** + * 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) +{ + /* Nothing to do */ + + 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. + * + **************************************************************************/ + +int nxplayer_main(int argc, char *argv[]) +{ + 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_detach(pPlayer); + nxplayer_release(pPlayer); + + return OK; +} |