summaryrefslogtreecommitdiff
path: root/nuttx/audio
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-10-27 07:44:53 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-10-27 07:44:53 -0600
commit66bc3094e689ff4a96c9ac2755335d45b8765aa1 (patch)
treea25e3cf4f1a61bcd727587fefc2725316c82ee05 /nuttx/audio
parent6dc974b4b0d8423cc9073e425a2e3a4370160df6 (diff)
downloadnuttx-66bc3094e689ff4a96c9ac2755335d45b8765aa1.tar.gz
nuttx-66bc3094e689ff4a96c9ac2755335d45b8765aa1.tar.bz2
nuttx-66bc3094e689ff4a96c9ac2755335d45b8765aa1.zip
Updated audio subsystem from Ken Pettit
Diffstat (limited to 'nuttx/audio')
-rw-r--r--nuttx/audio/Kconfig150
-rw-r--r--nuttx/audio/Makefile12
-rw-r--r--nuttx/audio/README.txt125
-rw-r--r--nuttx/audio/audio.c570
-rw-r--r--nuttx/audio/buffer.c59
5 files changed, 745 insertions, 171 deletions
diff --git a/nuttx/audio/Kconfig b/nuttx/audio/Kconfig
index 7ce503aaf..ef4bfe779 100644
--- a/nuttx/audio/Kconfig
+++ b/nuttx/audio/Kconfig
@@ -11,53 +11,64 @@ config AUDIO
if AUDIO
-config AUDIO_NCHANNELS
- int "Number of Audio Channels"
- default 2
+config AUDIO_MULTI_SESSION
+ bool "Support multiple sessions"
+ default n
---help---
- Stereo channel support is standard, but some systems may support 5 or 7
- channel capability.
+ Some audio devices, such as USB attached sound cards, may support more
+ than one streaming session at a time (each with one or more audio channels).
+ Selecting this feature adds support for tracking multiple concurrent
+ sessions with the lower-level audio devices.
config AUDIO_LARGE_BUFFERS
bool "Support Audio Buffers with greater than 65K samples"
default n
---help---
- By default, the Audio Pipeline Buffer uses a 16-bit max sample count, limiting
+ By default, the Audio Pipeline Buffers use a 16-bit max sample count, limiting
the number of samples per buffer to 65K. Enable this option to specify a
32-bit max sample count for increased samples / buffer capability.
channel capability.
-config AUDIO_CUSTOM_DEV_PATH
- bool "Use custom device path"
- default n
+config AUDIO_NUM_BUFFERS
+ int "Number of buffers for audio processing"
+ default 2
---help---
- By default, all audio devices on the target are are registered in the
- /dev/audio directory. Select this option to change the default location
- for the device registration.
-
-if AUDIO_CUSTOM_DEV_PATH
+ Specifies the number of buffers to allocate for audio processing.
+ If Driver Specified buffer sizes is enabled (below), then the
+ low-level drivers will have the opportunity to override this
+ value.
+
+config AUDIO_BUFFER_NUMBYTES
+ int "Size of each audio buffer for audio processing"
+ default 8192
+ ---help---
+ Specifies the allocation size for each audio buffer
+ If Driver Specified buffer sizes is enabled (below), then the
+ low-level drivers will have the opportunity to override this
+ value.
-config AUDIO_DEV_ROOT
- bool "Place audio devices in /dev"
+config AUDIO_DRIVER_SPECIFIC_BUFFERS
+ bool "Support for Driver specified buffer sizes"
default n
---help---
- This option causes all device entries to appear in /dev with all the
- other device entries. This option generates the smallest code and
- RAM footprint.
+ By default, the Audio system uses the same size and number of buffers
+ regardless of the specific audio device in use. Specifying 'y' here
+ adds extra code which allows the lower-level audio device to specify
+ a partucular size and number of buffers.
-if !AUDIO_DEV_ROOT
+menu "Supported Audio Formats"
-config AUDIO_DEV_PATH
- string "Base path for Audio devices"
- default "/dev/audio"
+config AUDIO_FORMAT_AC3
+ bool "AC3 Format"
+ default n
---help---
- The path on the target where audio devices are registered. The default
- is to place all audio devices in the /dev/audio/ directory.
+ Build in support for AC3 (Dolby Digital) Audio format.
-endif
-endif
-
-menu "Supported Audio Formats"
+config AUDIO_FORMAT_DTS
+ bool "DTS Format"
+ default n
+ ---help---
+ Add in support for DTS format.
config AUDIO_FORMAT_PCM
bool "PCM Audio"
@@ -67,10 +78,16 @@ config AUDIO_FORMAT_PCM
config AUDIO_FORMAT_MP3
bool "MPEG 3 Layer 1"
- default n
+ default y
---help---
Build in support for MP3 Audio format.
+config AUDIO_FORMAT_MIDI
+ bool "Midi Format"
+ default n
+ ---help---
+ Add in support for MIDI format.
+
config AUDIO_FORMAT_WMA
bool "WMA Format (see copyright notice)"
default n
@@ -85,6 +102,79 @@ config AUDIO_FORMAT_OGG_VORBIS
endmenu
+menu "Exclude Specific Audio Features"
+
+config AUDIO_EXCLUDE_VOLUME
+ bool "Exclude volume controls"
+ default n
+ ---help---
+ Exclude building support for changing the playback volume.
+
+config AUDIO_EXCLUDE_BALANCE
+ bool "Exclude balance controls"
+ default n
+ ---help---
+ Exclude building support for changing the balance.
+
+config AUDIO_EXCLUDE_TONE
+ bool "Exclude tone (bass and treble) controls"
+ default n
+ ---help---
+ Exclude building support for changing the bass and treble.
+
+config AUDIO_EXCLUDE_PAUSE_RESUME
+ bool "Exclude pause and resume controls"
+ default n
+ ---help---
+ Exclude building support for pausing and resuming audio files
+ once they are submitted. If the sound system is being used to play
+ short system notification or error type sounds that typicaly only
+ last a second or two, then there is no need (or chance) to pause or
+ resume sound playback once it has started.
+
+config AUDIO_EXCLUDE_STOP
+ bool "Exclude stop playback controls"
+ default n
+ ---help---
+ Exclude building support for stopping audio files once they are
+ submitted. If the sound system is being used to play short ssytem
+ notification or error type sounds that typicaly only last a second
+ or two, then there is no need (or chance) to stop the sound
+ playback once it has started.
+
+endmenu
+
+config AUDIO_CUSTOM_DEV_PATH
+ bool "Use custom device path"
+ default n
+ ---help---
+ By default, all audio devices on the target are are registered in the
+ /dev/audio directory. Select this option to change the default location
+ for the device registration.
+
+if AUDIO_CUSTOM_DEV_PATH
+
+config AUDIO_DEV_ROOT
+ bool "Place audio devices in /dev"
+ default n
+ ---help---
+ This option causes all device entries to appear in /dev with all the
+ other device entries. This option generates the smallest code and
+ RAM footprint.
+
+if !AUDIO_DEV_ROOT
+
+config AUDIO_DEV_PATH
+ string "Base path for Audio devices"
+ default "/dev/audio"
+ ---help---
+ The path on the target where audio devices are registered. The default
+ is to place all audio devices in the /dev/audio/ directory.
+
+endif
+endif
+
+
# These are here as placeholders of what could be added
if CONFIG_AUDIO_PLANNED
diff --git a/nuttx/audio/Makefile b/nuttx/audio/Makefile
index b6c111621..7b34de1ae 100644
--- a/nuttx/audio/Makefile
+++ b/nuttx/audio/Makefile
@@ -53,18 +53,6 @@ ifeq ($(CONFIG_AUDIO_FORMAT_PCM),y)
CSRCS += pcm.c
endif
-ifeq ($(CONFIG_AUDIO_MIXER),y)
- CSRCS += mixer.c
-endif
-
-ifeq ($(CONFIG_AUDIO_MIDI_SYNTH),y)
- CSRCS += midisynth.c
-endif
-
-ifeq ($(CONFIG_AUDIO_FONT),y)
- CSRCS += audio_font.c
-endif
-
AOBJS = $(ASRCS:.S=$(OBJEXT))
COBJS = $(CSRCS:.c=$(OBJEXT))
diff --git a/nuttx/audio/README.txt b/nuttx/audio/README.txt
new file mode 100644
index 000000000..80178865d
--- /dev/null
+++ b/nuttx/audio/README.txt
@@ -0,0 +1,125 @@
+README
+^^^^^^
+
+This directory contains the audio subsytem support for NuttX. The contents of this
+directory are only built if CONFIG_AUDIO is defined in the NuttX configuration file.
+
+Contents
+^^^^^^^^
+ - Files in this directory
+ - Related Header Files
+ - Related directories
+
+Files in this directory
+^^^^^^^^^^^^^^^^^^^^^^^
+
+This directory holds the NuttX audio subsystem upper-half. The upper-half provides
+a common interface for applications to interface with and also defines a bind
+layer for specific lower-half audio device drivers.
+
+ audio.c - The upper-half driver that binds to a lower-half driver from the
+ drivers/audio subdirectory. For each attached audio device, there
+ will be an instance of this upper-half driver bound to the
+ instance of the lower half driver context.
+ buffer.c - Routines to manage creattion and destruction of audio pipeline buffers
+ (apb) used in the audio subsystem. Audio pipeline buffers are passed
+ between user applications and the audio drivers to deliver audio
+ content for playback (or possibly recording in the future).
+ pcm.c - Routines to manage PCM / WAV type data. Currently just a placeholder.
+ README - This file!
+
+
+Related Header Files
+^^^^^^^^^^^^^^^^^^^^
+
+include/nuttx/audio/audio.h -- Top level include file defining the audio interface
+include/nuttx/audio/vs1053.h -- Specific driver initialization prototypes
+
+Configuration Settings
+^^^^^^^^^^^^^^^^^^^^^^
+
+General Audio Settings
+----------------------
+
+CONFIG_AUDIO
+ Enables overall support for audio subsystem
+CONFIG_AUDIO_MULTI_SESSION
+ Enables support for the audio subystem to track mutliple open sessions
+ with lower-level audio devices.
+CONFIG_AUDIO_LARGE_BUFFERS
+ Specifies that buffer size variables should be 32-bit vs. the normal 16-bit
+ size. This allows buffers to be larger than 64K bytes on systems with
+ an abundance of RAM.
+CONFIG_AUDIO_NUM_BUFFERS
+ Sets the number of audio buffers to use for audio operations. If the
+ configuration has set CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS, and an audio
+ device does not support the operation, then this becomes the default number
+ of buffers to use.
+CONFIG_AUDIO_BUFFER_SIZE
+ Sets the size of the audio buffers to use for audio operations. If the
+ configuration has set CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS, and an audio
+ device does not support the operation, then this becomes the default size
+ of buffers to use.
+CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+ Enables support for lower-level audio drivers to specify the number and size
+ of buffers that should be allocated for best performance while interacting
+ with that driver.
+CONFIG_AUDIO_CUSTOM_DEV_PATH
+ Specifies that all audio devices should be registered in the filesystem at
+ a location other than the standard /dev/audio directory.
+CONFIG_AUDIO_DEV_ROOT
+ Specifies that all audio devices should be registered in the /dev directory.
+ Saves a tiny bit of code and RAM space since an additional directory isn't needed,
+ but at the expense of execution speed when searching for audio devices since all
+ entries in /dev must be opened and tested if they provide audio support.
+ Available only if CONFIG_AUDIO_CUSTOM_DEV_PATH is selected.
+CONFIG_AUDIO_DEV_PATH
+ Specifies a custom directory where audio devices will be registered.
+ Available if CONFIG_AUDIO_CUSTOM_DEV_PATH is selected and CONFIG_AUDIO_DEV_ROOT
+ is not selected.
+
+
+Audio Format Support Selections
+-------------------------------
+
+CONFIG_AUDIO_FORMAT_AC3
+ Specifies that AC3 support should be enabled if available by a lower-half driver.
+CONFIG_AUDIO_FORMAT_DTS
+ Specifies that DTS support should be enabled if available by a lower-half driver.
+CONFIG_AUDIO_FORMAT_PCM
+ Specifies that PCM support should be enabled if available by a lower-half driver.
+CONFIG_AUDIO_FORMAT_MP3
+ Specifies that MP3 support should be enabled if available by a lower-half driver.
+CONFIG_AUDIO_FORMAT_MIDI
+ Specifies that MIDI support should be enabled if available by a lower-half driver.
+CONFIG_AUDIO_FORMAT_WMA
+ Specifies that WMA support should be enabled if available by a lower-half driver.
+CONFIG_AUDIO_FORMAT_OGG_VORBIS
+ Specifies that Ogg Vorbis support should be enabled if available by a lower-half driver.
+
+
+Audio feature exclusion Selections
+----------------------------------
+
+CONFIG_AUDIO_EXCLUDE_VOLUME
+ Disables support in all libraries and drivers for setting the playback volume. In
+ this case, the device volume will depend on the default level defined by the
+ lower-level driver, typically via a config setting.
+CONFIG_AUDIO_EXCLUDE_BALANCE
+ Disables support in all libraries and drivers for setting the playback balance.
+ Also, the volume support must not be excluded for balance to work or make sense.
+CONFIG_AUDIO_EXCLUDE_TONE
+ Disables support for setting bass and treble.
+CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+ Disables support in all libraries and drivers for pausing and resuming playback.
+CONFIG_AUDIO_EXCLUDE_STOP
+ Disables support in all libraries and drivers for stopping an audio playback
+ once it has started. Typically selected if only short notification audio sounds
+ are needed (vs. media playing type applications).
+
+Related Subdirectories
+^^^^^^^^^^^^^^^^^^^^^^
+
+drivers/audio -- Contains the lower-level device specific drivers.
+apps/system/nxplayer -- User-mode audio subsystem interface library.
+
diff --git a/nuttx/audio/audio.c b/nuttx/audio/audio.c
index 444412c97..351ed8ad9 100644
--- a/nuttx/audio/audio.c
+++ b/nuttx/audio/audio.c
@@ -60,6 +60,7 @@
#include <nuttx/fs/fs.h>
#include <nuttx/arch.h>
#include <nuttx/audio/audio.h>
+#include <mqueue.h>
#include <arch/irq.h>
@@ -75,6 +76,10 @@
# define AUDIO_MAX_DEVICE_PATH 32
#endif
+#ifndef CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO
+# define CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO 1
+#endif
+
/****************************************************************************
* Private Type Definitions
****************************************************************************/
@@ -84,10 +89,10 @@
struct audio_upperhalf_s
{
uint8_t crefs; /* The number of times the device has been opened */
- volatile bool started; /* True: pulsed output is being generated */
+ volatile bool started; /* True: playback is active */
sem_t exclsem; /* Supports mutual exclusion */
- struct audio_info_s info; /* Pulsed output characteristics */
FAR struct audio_lowerhalf_s *dev; /* lower-half state */
+ mqd_t usermq; /* User mode app's message queue */
};
/****************************************************************************
@@ -98,10 +103,16 @@ static int audio_open(FAR struct file *filep);
static int audio_close(FAR struct file *filep);
static ssize_t audio_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
static ssize_t audio_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
-static int audio_start(FAR struct audio_upperhalf_s *upper, unsigned int oflags);
static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int audio_start(FAR struct audio_upperhalf_s *upper, FAR void *session);
+static void audio_callback(FAR void *priv, uint16_t reason,
+ FAR struct ap_buffer_s *apb, uint16_t status, FAR void *session);
+#else
+static int audio_start(FAR struct audio_upperhalf_s *upper);
static void audio_callback(FAR void *priv, uint16_t reason,
FAR struct ap_buffer_s *apb, uint16_t status);
+#endif /* CONFIG_AUDIO_MULTI_SESSION */
/****************************************************************************
* Private Data
@@ -113,10 +124,10 @@ static const struct file_operations g_audioops =
audio_close, /* close */
audio_read, /* read */
audio_write, /* write */
- 0, /* seek */
+ 0, /* seek */
audio_ioctl /* ioctl */
#ifndef CONFIG_DISABLE_POLL
- , 0 /* poll */
+ , 0 /* poll */
#endif
};
@@ -134,10 +145,10 @@ static const struct file_operations g_audioops =
static int audio_open(FAR struct file *filep)
{
- FAR struct inode *inode = filep->f_inode;
+ FAR struct inode *inode = filep->f_inode;
FAR struct audio_upperhalf_s *upper = inode->i_private;
- uint8_t tmp;
- int ret;
+ uint8_t tmp;
+ int ret;
audvdbg("crefs: %d\n", upper->crefs);
@@ -167,6 +178,7 @@ static int audio_open(FAR struct file *filep)
/* Save the new open count on success */
upper->crefs = tmp;
+ upper->usermq = NULL;
ret = OK;
errout_with_sem:
@@ -186,9 +198,9 @@ errout:
static int audio_close(FAR struct file *filep)
{
- FAR struct inode *inode = filep->f_inode;
+ FAR struct inode *inode = filep->f_inode;
FAR struct audio_upperhalf_s *upper = inode->i_private;
- int ret;
+ int ret;
audvdbg("crefs: %d\n", upper->crefs);
@@ -243,7 +255,18 @@ errout:
static ssize_t audio_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
{
- /* Return zero -- usually meaning end-of-file */
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct audio_upperhalf_s *upper = inode->i_private;
+ FAR struct audio_lowerhalf_s *lower = upper->dev;
+
+ /* TODO: Should we check permissions here? */
+
+ /* Audio read operations get passed directly to the lower-level */
+
+ if (lower->ops->read != NULL)
+ {
+ return lower->ops->read(lower, buffer, buflen);
+ }
return 0;
}
@@ -258,6 +281,19 @@ static ssize_t audio_read(FAR struct file *filep, FAR char *buffer, size_t bufle
static ssize_t audio_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct audio_upperhalf_s *upper = inode->i_private;
+ FAR struct audio_lowerhalf_s *lower = upper->dev;
+
+ /* TODO: Should we check permissions here? */
+
+ /* Audio write operations get passed directly to the lower-level */
+
+ if (lower->ops->write != NULL)
+ {
+ return lower->ops->write(lower, buffer, buflen);
+ }
+
return 0;
}
@@ -269,7 +305,11 @@ static ssize_t audio_write(FAR struct file *filep, FAR const char *buffer, size_
*
************************************************************************************/
-static int audio_start(FAR struct audio_upperhalf_s *upper, unsigned int oflags)
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int audio_start(FAR struct audio_upperhalf_s *upper, FAR void *session)
+#else
+static int audio_start(FAR struct audio_upperhalf_s *upper)
+#endif
{
FAR struct audio_lowerhalf_s *lower = upper->dev;
int ret = OK;
@@ -280,17 +320,21 @@ static int audio_start(FAR struct audio_upperhalf_s *upper, unsigned int oflags)
if (!upper->started)
{
- /* Invoke the bottom half method to start the pulse train */
+ /* Invoke the bottom half method to start the audio stream */
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->start(lower, session);
+#else
ret = lower->ops->start(lower);
+#endif
- /* A return value of zero means that the pulse train was started
+ /* A return value of zero means that the audio stream was started
* successfully.
*/
if (ret == OK)
{
- /* Indicate that the pulse train has started */
+ /* Indicate that the audio stream has started */
upper->started = true;
}
@@ -309,10 +353,14 @@ static int audio_start(FAR struct audio_upperhalf_s *upper, unsigned int oflags)
static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
- FAR struct inode *inode = filep->f_inode;
+ FAR struct inode *inode = filep->f_inode;
FAR struct audio_upperhalf_s *upper = inode->i_private;
FAR struct audio_lowerhalf_s *lower = upper->dev;
- int ret;
+ FAR struct audio_buf_desc_s *bufdesc;
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ FAR void *session;
+#endif
+ int ret;
audvdbg("cmd: %d arg: %ld\n", cmd, arg);
@@ -347,14 +395,19 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
case AUDIOIOC_CONFIGURE:
{
- FAR const struct audio_caps_s *caps = (FAR const struct audio_caps_s*)((uintptr_t)arg);
+ FAR const struct audio_caps_desc_s *caps =
+ (FAR const struct audio_caps_desc_s*)((uintptr_t)arg);
DEBUGASSERT(lower->ops->configure != NULL);
- audvdbg("AUDIOIOC_INITIALIZE: Device=%d", caps->ac_type);
+ audvdbg("AUDIOIOC_INITIALIZE: Device=%d", caps->caps.ac_type);
/* Call the lower-half driver configure handler */
- ret = lower->ops->configure(lower, caps, &audio_callback, upper);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->configure(lower, caps->session, &caps->caps);
+#else
+ ret = lower->ops->configure(lower, &caps->caps);
+#endif
}
break;
@@ -369,10 +422,10 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
}
break;
- /* AUDIOIOC_START - Start the pulsed output. The AUDIOIOC_SETCHARACTERISTICS
+ /* AUDIOIOC_START - Start the audio stream. The AUDIOIOC_SETCHARACTERISTICS
* command must have previously been sent.
*
- * ioctl argument: None
+ * ioctl argument: Audio session
*/
case AUDIOIOC_START:
@@ -380,17 +433,23 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
audvdbg("AUDIOIOC_START\n");
DEBUGASSERT(lower->ops->start != NULL);
- /* Start the pulse train */
+ /* Start the audio stream */
- ret = audio_start(upper, filep->f_oflags);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ session = (FAR void *) arg;
+ ret = audio_start(upper, session);
+#else
+ ret = audio_start(upper);
+#endif
}
break;
- /* AUDIOIOC_STOP - Stop the pulsed output.
+ /* AUDIOIOC_STOP - Stop the audio stream.
*
- * ioctl argument: None
+ * ioctl argument: Audio session
*/
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
case AUDIOIOC_STOP:
{
audvdbg("AUDIOIOC_STOP\n");
@@ -398,11 +457,191 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
if (upper->started)
{
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ session = (FAR void *) arg;
+ ret = lower->ops->stop(lower, session);
+#else
ret = lower->ops->stop(lower);
+#endif
upper->started = false;
}
}
break;
+#endif /* CONFIG_AUDIO_EXCLUDE_STOP */
+
+ /* AUDIOIOC_PAUSE - Pause the audio stream.
+ *
+ * ioctl argument: Audio session
+ */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+
+ case AUDIOIOC_PAUSE:
+ {
+ audvdbg("AUDIOIOC_PAUSE\n");
+ DEBUGASSERT(lower->ops->pause != NULL);
+
+ if (upper->started)
+ {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ session = (FAR void *) arg;
+ ret = lower->ops->pause(lower, session);
+#else
+ ret = lower->ops->pause(lower);
+#endif
+ }
+ }
+ break;
+
+ /* AUDIOIOC_RESUME - Resume the audio stream.
+ *
+ * ioctl argument: Audio session
+ */
+
+ case AUDIOIOC_RESUME:
+ {
+ audvdbg("AUDIOIOC_RESUME\n");
+ DEBUGASSERT(lower->ops->resume != NULL);
+
+ if (upper->started)
+ {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ session = (FAR void *) arg;
+ ret = lower->ops->resume(lower, session);
+#else
+ ret = lower->ops->resume(lower);
+#endif
+ }
+ }
+ break;
+
+#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
+
+ /* AUDIOIOC_ALLOCBUFFER - Allocate an audio buffer
+ *
+ * ioctl argument: pointer to an audio_buf_desc_s structure
+ */
+
+ case AUDIOIOC_ALLOCBUFFER:
+ {
+ audvdbg("AUDIOIOC_ALLOCBUFFER\n");
+
+ bufdesc = (FAR struct audio_buf_desc_s *) arg;
+ if (lower->ops->allocbuffer)
+ {
+ ret = lower->ops->allocbuffer(lower, bufdesc);
+ }
+ else
+ {
+ /* Perform a simple kumalloc operation assuming 1 session */
+
+ ret = apb_alloc(bufdesc);
+ }
+ }
+ break;
+
+ /* AUDIOIOC_FREEBUFFER - Free an audio buffer
+ *
+ * ioctl argument: pointer to an audio_buf_desc_s structure
+ */
+
+ case AUDIOIOC_FREEBUFFER:
+ {
+ audvdbg("AUDIOIOC_FREEBUFFER\n");
+
+ bufdesc = (FAR struct audio_buf_desc_s *) arg;
+ if (lower->ops->freebuffer)
+ {
+ ret = lower->ops->freebuffer(lower, bufdesc);
+ }
+ else
+ {
+ /* Perform a simple kufree operation */
+
+ DEBUGASSERT(bufdesc->u.pBuffer != NULL);
+ apb_free(bufdesc->u.pBuffer);
+ ret = sizeof(struct audio_buf_desc_s);
+ }
+ }
+ break;
+
+ /* AUDIOIOC_ENQUEUEBUFFER - Enqueue an audio buffer
+ *
+ * ioctl argument: pointer to an audio_buf_desc_s structure
+ */
+
+ case AUDIOIOC_ENQUEUEBUFFER:
+ {
+ audvdbg("AUDIOIOC_ENQUEUEBUFFER\n");
+
+ DEBUGASSERT(lower->ops->enqueuebuffer != NULL);
+
+ bufdesc = (FAR struct audio_buf_desc_s *) arg;
+ ret = lower->ops->enqueuebuffer(lower, bufdesc->u.pBuffer);
+ }
+ break;
+
+ /* AUDIOIOC_REGISTERMQ - Register a client Message Queue
+ *
+ * TODO: This needs to have multi session support.
+ */
+
+ case AUDIOIOC_REGISTERMQ:
+ {
+ upper->usermq = (mqd_t) arg;
+ ret = OK;
+ }
+ break;
+
+ /* AUDIOIOC_UNREGISTERMQ - Register a client Message Queue
+ *
+ * TODO: This needs to have multi session support.
+ */
+
+ case AUDIOIOC_UNREGISTERMQ:
+ {
+ upper->usermq = NULL;
+ ret = OK;
+ }
+ break;
+
+ /* AUDIOIOC_RESERVE - Reserve a session with the driver
+ *
+ * ioctl argument - pointer to receive the session context
+ */
+
+ case AUDIOIOC_RESERVE:
+ {
+ DEBUGASSERT(lower->ops->reserve != NULL);
+
+ /* Call lower-half to perform the reservation */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->reserve(lower, (FAR void **) arg);
+#else
+ ret = lower->ops->reserve(lower);
+#endif
+ }
+ break;
+
+ /* AUDIOIOC_RESERVE - Reserve a session with the driver
+ *
+ * ioctl argument - pointer to receive the session context
+ */
+
+ case AUDIOIOC_RELEASE:
+ {
+ DEBUGASSERT(lower->ops->release != NULL);
+
+ /* Call lower-half to perform the release */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->release(lower, (FAR void *) arg);
+#else
+ ret = lower->ops->release(lower);
+#endif
+ }
+ break;
/* Any unrecognized IOCTL commands might be platform-specific ioctl commands */
@@ -420,6 +659,182 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
}
/****************************************************************************
+ * Name: audio_dequeuebuffer
+ *
+ * Description:
+ * Dequeues a previously enqueued Audio Pipeline Buffer.
+ *
+ * 1. The upper half driver calls the enqueuebuffer method, providing the
+ * lower half driver with the ab_buffer to process.
+ * 2. The lower half driver's enqueuebuffer will either processes the
+ * buffer directly, or more likely add it to a queue for processing
+ * by a background thread or worker task.
+ * 3. When the lower half driver has completed processing of the enqueued
+ * ab_buffer, it will call this routine to indicate processing of the
+ * buffer is complete.
+ * 4. When this routine is called, it will check if any threads are waiting
+ * to enqueue additional buffers and "wake them up" for further
+ * processing.
+ *
+ * Input parameters:
+ * handle - This is the handle that was provided to the lower-half
+ * start() method.
+ * apb - A pointer to the previsously enqueued ap_buffer_s
+ * status - Status of the dequeue operation
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static inline void audio_dequeuebuffer(FAR struct audio_upperhalf_s *upper,
+ FAR struct ap_buffer_s *apb, uint16_t status,
+ FAR void *session)
+#else
+static inline void audio_dequeuebuffer(FAR struct audio_upperhalf_s *upper,
+ FAR struct ap_buffer_s *apb, uint16_t status)
+#endif
+{
+ struct audio_msg_s msg;
+
+ audllvdbg("Entry\n");
+
+ /* Send a dequeue message to the user if a message queue is registered */
+
+ if (upper->usermq != NULL)
+ {
+ msg.msgId = AUDIO_MSG_DEQUEUE;
+ msg.u.pPtr = apb;
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ msg.session = session;
+#endif
+ apb->flags |= AUDIO_APB_DEQUEUED;
+ mq_send(upper->usermq, &msg, sizeof(msg), CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO);
+ }
+}
+
+/****************************************************************************
+ * Name: audio_complete
+ *
+ * Description:
+ * Send an AUDIO_MSG_COMPLETE message to the client to indicate that the
+ * active playback has completed. The lower-half driver initiates this
+ * call via its callback pointer to our upper-half driver.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static inline void audio_complete(FAR struct audio_upperhalf_s *upper,
+ FAR struct ap_buffer_s *apb, uint16_t status,
+ FAR void *session)
+#else
+static inline void audio_complete(FAR struct audio_upperhalf_s *upper,
+ FAR struct ap_buffer_s *apb, uint16_t status)
+#endif
+{
+ struct audio_msg_s msg;
+
+ audllvdbg("Entry\n");
+
+ /* Send a dequeue message to the user if a message queue is registered */
+
+ upper->started = false;
+ if (upper->usermq != NULL)
+ {
+ msg.msgId = AUDIO_MSG_COMPLETE;
+ msg.u.pPtr = NULL;
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ msg.session = session;
+#endif
+ mq_send(upper->usermq, &msg, sizeof(msg),
+ CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO);
+ }
+}
+
+/****************************************************************************
+ * Name: audio_callback
+ *
+ * Description:
+ * Provides a callback interface for lower-half drivers to call to the
+ * upper-half for buffer dequeueing, error reporting, etc.
+ *
+ * Input parameters:
+ * priv - Private context data owned by the upper-half
+ * reason - The reason code for the callback
+ * apb - A pointer to the previsously enqueued ap_buffer_s
+ * status - Status information associated with the callback
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static void audio_callback(FAR void *handle, uint16_t reason,
+ FAR struct ap_buffer_s *apb, uint16_t status,
+ FAR void *session)
+#else
+static void audio_callback(FAR void *handle, uint16_t reason,
+ FAR struct ap_buffer_s *apb, uint16_t status)
+#endif
+{
+ FAR struct audio_upperhalf_s *upper = (FAR struct audio_upperhalf_s *)handle;
+
+ audllvdbg("Entry\n");
+
+ /* Perform operation based on reason code */
+
+ switch (reason)
+ {
+ case AUDIO_CALLBACK_DEQUEUE:
+ {
+ /* Call the dequeue routine */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ audio_dequeuebuffer(upper, apb, status, session);
+#else
+ audio_dequeuebuffer(upper, apb, status);
+#endif
+ break;
+ }
+
+ /* Lower-half I/O error occurred */
+
+ case AUDIO_CALLBACK_IOERR:
+ {
+ }
+ break;
+
+ /* Lower-half driver has completed a playback */
+
+ case AUDIO_CALLBACK_COMPLETE:
+ {
+ /* Send a complete message to the user if a message queue is registered */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ audio_complete(upper, apb, status, session);
+#else
+ audio_complete(upper, apb, status);
+#endif
+ }
+ break;
+
+ default:
+ {
+ auddbg("Unknown callback reason code %d\n", reason);
+ break;
+ }
+ }
+}
+
+/****************************************************************************
* Public Functions
****************************************************************************/
@@ -451,14 +866,14 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
int audio_register(FAR const char *name, FAR struct audio_lowerhalf_s *dev)
{
FAR struct audio_upperhalf_s *upper;
- char path[AUDIO_MAX_DEVICE_PATH];
+ char path[AUDIO_MAX_DEVICE_PATH];
static bool dev_audio_created = false;
#ifndef CONFIG_AUDIO_CUSTOM_DEV_PATH
const char* devname = "/dev/audio";
#elif !defined(CONFIG_AUDIO_DEV_ROOT)
const char* devname = CONFIG_AUDIO_DEV_PATH;
const char* ptr;
- char* pathptr;
+ char* pathptr;
#endif
/* Allocate the upper-half data structure */
@@ -570,102 +985,13 @@ int audio_register(FAR const char *name, FAR struct audio_lowerhalf_s *dev)
strncat(path, name, AUDIO_MAX_DEVICE_PATH - 11);
#endif
- audvdbg("Registering %s\n", path);
- return register_driver(path, &g_audioops, 0666, upper);
-}
+ /* Give the lower-half a context to the upper half */
-/****************************************************************************
- * Name: audio_dequeuebuffer
- *
- * Description:
- * Dequeues a previously enqueued Audio Pipeline Buffer.
- *
- * 1. The upper half driver calls the enqueuebuffer method, providing the
- * lower half driver with the ab_buffer to process.
- * 2. The lower half driver's enqueuebuffer will either processes the
- * buffer directly, or more likely add it to a queue for processing
- * by a background thread or worker task.
- * 3. When the lower half driver has completed processing of the enqueued
- * ab_buffer, it will call this routine to indicated processing of the
- * buffer is complete.
- * 4. When this routine is called, it will check if any threads are waiting
- * to enqueue additional buffers and "wake them up" for further
- * processing.
- *
- * Input parameters:
- * handle - This is the handle that was provided to the lower-half
- * start() method.
- * apb - A pointer to the previsously enqueued ap_buffer_s
- * status - Status of the dequeue operation
- *
- * Returned Value:
- * None
- *
- * Assumptions:
- * This function may be called from an interrupt handler.
- *
- ****************************************************************************/
-
-static inline void audio_dequeuebuffer(FAR struct audio_upperhalf_s *upper,
- FAR struct ap_buffer_s *apb, uint16_t status)
-{
- audllvdbg("Entry\n");
-
- /* TODO: Implement the logic */
-
-}
-
-/****************************************************************************
- * Name: audio_callback
- *
- * Description:
- * Provides a callback interface for lower-half drivers to call to the
- * upper-half for buffer dequeueing, error reporting, etc.
- *
- * Input parameters:
- * priv - Private context data owned by the upper-half
- * reason - The reason code for the callback
- * apb - A pointer to the previsously enqueued ap_buffer_s
- * status - Status information associated with the callback
- *
- * Returned Value:
- * None
- *
- * Assumptions:
- * This function may be called from an interrupt handler.
- *
- ****************************************************************************/
-
-static void audio_callback(FAR void *handle, uint16_t reason,
- FAR struct ap_buffer_s *apb, uint16_t status)
-{
- FAR struct audio_upperhalf_s *upper = (FAR struct audio_upperhalf_s *)handle;
-
- audllvdbg("Entry\n");
-
- /* Perform operation based on reason code */
-
- switch (reason)
- {
- case AUDIO_CALLBACK_DEQUEUE:
- {
- /* Call the dequeue routine */
-
- audio_dequeuebuffer(upper, apb, status);
- break;
- }
-
- case AUDIO_CALLBACK_IOERR:
- {
- }
- break;
+ dev->upper = audio_callback;
+ dev->priv = upper;
- default:
- {
- auddbg("Unknown callback reason code %d\n", reason);
- break;
- }
- }
+ audvdbg("Registering %s\n", path);
+ return register_driver(path, &g_audioops, 0666, upper);
}
#endif /* CONFIG_AUDIO */
diff --git a/nuttx/audio/buffer.c b/nuttx/audio/buffer.c
index 3906891f6..b0b5c6c36 100644
--- a/nuttx/audio/buffer.c
+++ b/nuttx/audio/buffer.c
@@ -84,11 +84,11 @@
*
****************************************************************************/
-static void apb_semtake(sem_t *sem)
+static void apb_semtake(FAR struct ap_buffer_s *apb)
{
/* Take the semaphore (perhaps waiting) */
- while (sem_wait(sem) != 0)
+ while (sem_wait(&apb->sem) != 0)
{
/* The only case that an error should occr here is if
* the wait was awakened by a signal.
@@ -102,7 +102,7 @@ static void apb_semtake(sem_t *sem)
* Name: apb_semgive
****************************************************************************/
-#define apb_semgive(s) sem_post(s)
+#define apb_semgive(b) sem_post(&b->sem)
/****************************************************************************
* Name: apb_alloc
@@ -113,11 +113,41 @@ static void apb_semtake(sem_t *sem)
*
****************************************************************************/
-FAR struct ap_buffer_s *apb_alloc(int type, int sampleCount)
+int apb_alloc(FAR struct audio_buf_desc_s * bufdesc)
{
- /* TODO: Implement the alloc logic */
+ uint32_t bufsize;
+ int ret;
+ struct ap_buffer_s *pBuf;
- return NULL;
+ DEBUGASSERT(bufdesc->u.ppBuffer != NULL);
+
+ /* Perform a user mode allocation */
+
+ bufsize = sizeof(struct ap_buffer_s) + bufdesc->numbytes;
+ pBuf = kumalloc(bufsize);
+ *bufdesc->u.ppBuffer = pBuf;
+
+ /* Test if the allocation was successful or not */
+
+ if (*bufdesc->u.ppBuffer == NULL)
+ ret = -ENOMEM;
+ else
+ {
+ /* Populate the buffer contents */
+
+ memset(pBuf, bufsize, 0);
+ pBuf->i.channels = 1;
+ pBuf->crefs = 1;
+ pBuf->nmaxbytes = bufdesc->numbytes;
+ pBuf->nbytes = 0;
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ pBuf->session = bufdesc->session;
+#endif
+ sem_init(&pBuf->sem, 0, 1);
+ ret = sizeof(struct audio_buf_desc_s);
+ }
+
+ return ret;
}
/****************************************************************************
@@ -143,8 +173,19 @@ void apb_prepare(FAR struct ap_buffer_s *apb, int8_t allocmode, uint8_t format,
void apb_free(FAR struct ap_buffer_s *apb)
{
+ int refcount;
+
/* Perform a reference count decrement and possibly release the memory */
+ apb_semtake(apb);
+ refcount = apb->crefs--;
+ apb_semgive(apb);
+
+ if (refcount == 1)
+ {
+ auddbg("Freeing %p\n", apb);
+ kufree(apb);
+ }
}
/****************************************************************************
@@ -158,7 +199,11 @@ void apb_free(FAR struct ap_buffer_s *apb)
void apb_reference(FAR struct ap_buffer_s *apb)
{
- /* TODO: Implement the reference logic */
+ /* Do we need any thread protection here? Almost certaily... */
+
+ apb_semtake(apb);
+ apb->crefs++;
+ apb_semgive(apb);
}
#endif /* CONFIG_AUDIO */