From 0f1733022fd4500aeee55cab42f7c324a8a53eed Mon Sep 17 00:00:00 2001 From: Bongryul Lee Date: Thu, 8 Jun 2017 06:07:24 +0900 Subject: [PATCH] examples/nxplayer: add nxplayer from NuttX add command line media player. Change-Id: I8ee94dff717833e0d64ac9655502fba663110f1d Signed-off-by: Gregory Nutt [Lee: backported from NuttX] Signed-off-by: Bongryul Lee Signed-off-by: Junhwan Park --- apps/examples/nxplayer/Kconfig | 117 ++ apps/examples/nxplayer/Make.defs | 22 + apps/examples/nxplayer/Makefile | 158 +++ apps/examples/nxplayer/README.txt | 17 + apps/examples/nxplayer/nxplayer.c | 1974 ++++++++++++++++++++++++++++++++ apps/examples/nxplayer/nxplayer_main.c | 691 +++++++++++ apps/include/nxplayer.h | 488 ++++++++ 7 files changed, 3467 insertions(+) create mode 100644 apps/examples/nxplayer/Kconfig create mode 100644 apps/examples/nxplayer/Make.defs create mode 100644 apps/examples/nxplayer/Makefile create mode 100644 apps/examples/nxplayer/README.txt create mode 100644 apps/examples/nxplayer/nxplayer.c create mode 100644 apps/examples/nxplayer/nxplayer_main.c create mode 100644 apps/include/nxplayer.h diff --git a/apps/examples/nxplayer/Kconfig b/apps/examples/nxplayer/Kconfig new file mode 100644 index 0000000..86d0613 --- /dev/null +++ b/apps/examples/nxplayer/Kconfig @@ -0,0 +1,117 @@ +# +# For a description of the syntax of this configuration file, +# see kconfig-language at https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt +# + +config EXAMPLES_NXPLAYER + bool "NxPlayer Media Player" + default n + depends on AUDIO + ---help--- + Enable support for the NxPlayer media player library and optional + command line interface. + +if EXAMPLES_NXPLAYER + +config NXPLAYER_PLAYTHREAD_STACKSIZE + int "NxPlayer thread stack size" + default 1500 + ---help--- + Stack size to use with the NxPlayer play thread. + +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/examples/nxplayer/Make.defs b/apps/examples/nxplayer/Make.defs new file mode 100644 index 0000000..ca4a04b --- /dev/null +++ b/apps/examples/nxplayer/Make.defs @@ -0,0 +1,22 @@ +########################################################################### +# +# 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. +# +########################################################################### + +ifeq ($(CONFIG_SYSTEM_NXPLAYER),y) +CONFIGURED_APPS += examples/nxplayer +endif + diff --git a/apps/examples/nxplayer/Makefile b/apps/examples/nxplayer/Makefile new file mode 100644 index 0000000..cdf0e8a --- /dev/null +++ b/apps/examples/nxplayer/Makefile @@ -0,0 +1,158 @@ +########################################################################### +# +# 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/Makefile +# +# Copyright (C) 2013 Ken Pettit. All rights reserved. +# Copyright (C) 2012-2013, 2016 Gregory Nutt. All rights reserved. +# Author: Ken Pettit +# Gregory Nutt +# +# 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. +# +############################################################################ + + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +# built-in application info + +APPNAME = nxplayer +THREADEXEC = TASH_EXECMD_SYNC + +ASRCS = +CSRCS = nxplayer.c +MAINSRC = nxplayer_main.c + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) +MAINOBJ = $(MAINSRC:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) $(MAINSRC) +OBJS = $(AOBJS) $(COBJS) + +ifneq ($(CONFIG_BUILD_KERNEL),y) + OBJS += $(MAINOBJ) +endif + +ifeq ($(CONFIG_WINDOWS_NATIVE),y) + BIN = ..\..\libapps$(LIBEXT) +else +ifeq ($(WINTOOL),y) + BIN = ..\\..\\libapps$(LIBEXT) +else + BIN = ../../libapps$(LIBEXT) +endif +endif + +ifeq ($(WINTOOL),y) + INSTALL_DIR = "${shell cygpath -w $(BIN_DIR)}" +else + INSTALL_DIR = $(BIN_DIR) +endif + +CONFIG_EXAMPLES_NXPLAYER_PROGNAME ?= nxplayer$(EXEEXT) +PROGNAME = $(CONFIG_EXAMPLES_NXPLAYER_PROGNAME) + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: clean depend distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS) $(MAINOBJ): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + $(call ARCHIVE, $(BIN), $(OBJS)) + @touch .built + +ifeq ($(CONFIG_BUILD_KERNEL),y) +$(BIN_DIR)$(DELIM)$(PROGNAME): $(OBJS) $(MAINOBJ) + @echo "LD: $(PROGNAME)" + $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $(INSTALL_DIR)$(DELIM)$(PROGNAME) $(ARCHCRT0OBJ) $(MAINOBJ) $(LDLIBS) + $(Q) $(NM) -u $(INSTALL_DIR)$(DELIM)$(PROGNAME) + +install: $(BIN_DIR)$(DELIM)$(PROGNAME) + +else +install: + +endif + +ifeq ($(CONFIG_BUILTIN_APPS)$(CONFIG_SYSTEM_NXPLAYER),yy) +$(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat: $(DEPCONFIG) Makefile + $(call REGISTER,$(APPNAME),$(APPNAME)_main,$(THREADEXEC)) + +context: $(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat + +else +context: + +endif + +.depend: Makefile $(SRCS) + @$(MKDEP) $(ROOTDEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + $(call DELFILE, .built) + $(call CLEAN) + +distclean: clean + $(call DELFILE, Make.dep) + $(call DELFILE, .depend) + +-include Make.dep +.PHONY: preconfig +preconfig: diff --git a/apps/examples/nxplayer/README.txt b/apps/examples/nxplayer/README.txt new file mode 100644 index 0000000..32d1f20 --- /dev/null +++ b/apps/examples/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/examples/nxplayer/nxplayer.c b/apps/examples/nxplayer/nxplayer.c new file mode 100644 index 0000000..1eb93a0 --- /dev/null +++ b/apps/examples/nxplayer/nxplayer.c @@ -0,0 +1,1974 @@ +/**************************************************************************** + * + * 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 + * + * With ongoing support: + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Greory Nutt + * + * 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/**************************************************************************** + * 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 */ diff --git a/apps/examples/nxplayer/nxplayer_main.c b/apps/examples/nxplayer/nxplayer_main.c new file mode 100644 index 0000000..6e5e995 --- /dev/null +++ b/apps/examples/nxplayer/nxplayer_main.c @@ -0,0 +1,691 @@ +/**************************************************************************** + * + * 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 + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/**************************************************************************** + * 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; +} diff --git a/apps/include/nxplayer.h b/apps/include/nxplayer.h new file mode 100644 index 0000000..2b3e30e --- /dev/null +++ b/apps/include/nxplayer.h @@ -0,0 +1,488 @@ +/**************************************************************************** + * + * 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 + * + * With updates, enhancements, and modifications by: + * + * Author: Gregory Nutt + * + * 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 + +/**************************************************************************** + * 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 */ -- 2.7.4