aboutsummaryrefslogtreecommitdiffstats
path: root/src/gaudio
diff options
context:
space:
mode:
authorinmarket <andrewh@inmarket.com.au>2014-03-11 17:13:31 +1000
committerinmarket <andrewh@inmarket.com.au>2014-03-11 17:13:31 +1000
commitea5a1b849df6e5085a92957ad387f9e653674415 (patch)
tree72ede5ed78263a6fdba25039398b5c2a55bd1d3a /src/gaudio
parent944c33cbff5f2cfb1c80f48193aa2161574864fd (diff)
downloaduGFX-ea5a1b849df6e5085a92957ad387f9e653674415.tar.gz
uGFX-ea5a1b849df6e5085a92957ad387f9e653674415.tar.bz2
uGFX-ea5a1b849df6e5085a92957ad387f9e653674415.zip
Combine GAUDIN and GAUDOUT into a single GAUDIO module.
Simplify GAUDIN (now GAUDIO RECORD) api. Update audio demo's to match. Port Win32 driver to new audio api.
Diffstat (limited to 'src/gaudio')
-rw-r--r--src/gaudio/driver_play.h126
-rw-r--r--src/gaudio/driver_record.h108
-rw-r--r--src/gaudio/gaudio.c318
-rw-r--r--src/gaudio/sys_defs.h340
-rw-r--r--src/gaudio/sys_make.mk1
-rw-r--r--src/gaudio/sys_options.h44
-rw-r--r--src/gaudio/sys_rules.h54
7 files changed, 991 insertions, 0 deletions
diff --git a/src/gaudio/driver_play.h b/src/gaudio/driver_play.h
new file mode 100644
index 00000000..72ad4747
--- /dev/null
+++ b/src/gaudio/driver_play.h
@@ -0,0 +1,126 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gaudio/driver_play.h
+ * @brief GAUDIO - Audio play driver header file.
+ *
+ * @defgroup Driver Driver
+ * @ingroup GAUDIO
+ * @{
+ */
+
+#ifndef _GAUDIO_PLAY_LLD_H
+#define _GAUDIO_PLAY_LLD_H
+
+#include "gfx.h"
+
+#if (GFX_USE_GAUDIO && GAUDIO_NEED_PLAY) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Type definitions */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Get a block of audio data to play
+ * @return A pointer to the GAaudioData structure or NULL if none is currently available
+ *
+ * @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers.
+ *
+ * @iclass
+ * @notapi
+ */
+GAudioData *gaudioPlayGetDataBlockI(void);
+
+/**
+ * @brief Release a block of audio data to the free list
+ *
+ * @param[in] paud The GAudioData block to be released.
+ *
+ * @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers.
+ *
+ * @iclass
+ * @notapi
+ */
+void gaudioPlayReleaseDataBlockI(GAudioData *paud);
+
+/**
+ * @brief Signal that all playing has now stopped
+ *
+ * @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers.
+ *
+ * @iclass
+ * @notapi
+ */
+void gaudioPlayDoneI(void);
+
+/**
+ * @brief Initialise the play driver
+ * @return TRUE if the channel, frequency and format are valid.
+ *
+ * @param[in] channel The channel to use (see the driver for the available channels provided)
+ * @param[in] frequency The sample frequency to use
+ * @param[in] format The sample format
+ *
+ * @note The driver will always have been stopped and de-init before this is called.
+ *
+ * @api
+ */
+bool_t gaudio_play_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format);
+
+/**
+ * @brief Start the audio output playing
+ *
+ * @note This may be called at any stage including while the driver
+ * is already playing. The driver should check for data blocks
+ * to play using @p gaudioPlayGetDataBlockI().
+ *
+ * @api
+ */
+void gaudio_play_lld_start(void);
+
+/**
+ * @brief Stop the audio output playing.
+ *
+ * @note Some drivers may only stop playing at a data block boundary.
+ * @note It is possible but unlikely for it to be called when playing has already stopped.
+ * @note It should not return until all active buffers (currently in use by the driver)
+ * have been returned to the free-list and @p gaudioPlayDoneI() has been called.
+ *
+ * @api
+ */
+void gaudio_play_lld_stop(void);
+
+/**
+ * @brief Set the output volume.
+ * @return TRUE if successful.
+ *
+ * @param[in] 0->255 (0 = muted)
+ *
+ * @note Some drivers may not support this. They will return FALSE.
+ * @note For stereo devices, both channels are set to the same volume.
+ *
+ * @api
+ */
+bool_t gaudio_play_lld_set_volume(uint8_t vol);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_PLAY */
+
+#endif /* _GAUDIO_PLAY_LLD_H */
+/** @} */
diff --git a/src/gaudio/driver_record.h b/src/gaudio/driver_record.h
new file mode 100644
index 00000000..252cae5c
--- /dev/null
+++ b/src/gaudio/driver_record.h
@@ -0,0 +1,108 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gaudio/driver_record.h
+ * @brief GAUDIO - Audio Recording driver header file.
+ *
+ * @defgroup Driver Driver
+ * @ingroup GAUDIO
+ * @{
+ */
+
+#ifndef _GAUDIO_RECORD_LLD_H
+#define _GAUDIO_RECORD_LLD_H
+
+#include "gfx.h"
+
+#if (GFX_USE_GAUDIO && GAUDIO_NEED_RECORD) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Type definitions */
+/*===========================================================================*/
+
+/**
+ * @brief Get a free block of audio data that we can record into
+ * @return A pointer to the GAaudioData structure or NULL if none is currently available
+ *
+ * @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers.
+ *
+ * @iclass
+ * @notapi
+ */
+GAudioData *gaudioRecordGetFreeBlockI(void);
+
+/**
+ * @brief Save a block of recorded audio data ready for the application
+ *
+ * @param[in] paud The GAudioData block with data.
+ *
+ * @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers.
+ *
+ * @iclass
+ * @notapi
+ */
+void gaudioRecordSaveDataBlockI(GAudioData *paud);
+
+/**
+ * @brief Signal that all recording has now stopped
+ *
+ * @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers.
+ *
+ * @iclass
+ * @notapi
+ */
+void gaudioRecordDoneI(void);
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Initialise the record driver
+ * @return TRUE if the channel, frequency and format are valid.
+ *
+ * @param[in] channel The channel to use (see the driver for the available channels provided)
+ * @param[in] frequency The sample frequency to use
+ * @param[in] format The sample format
+ *
+ * @note The driver will always have been stopped and de-init before this is called.
+ *
+ * @api
+ */
+bool_t gaudio_record_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format);
+
+/**
+ * @brief Start the audio recording
+ *
+ * @api
+ */
+void gaudio_record_lld_start(void);
+
+/**
+ * @brief Stop the audio recording.
+ *
+ * @note Some drivers may only stop recording at a data block boundary.
+ * @note This routine should not return until any currently active buffers have been
+ * saved (even if with zero length) and @p gaudioRecordDoneI() has been called.
+ *
+ * @api
+ */
+void gaudio_record_lld_stop(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_RECORD */
+
+#endif /* _GAUDIO_RECORD_LLD_H */
+/** @} */
diff --git a/src/gaudio/gaudio.c b/src/gaudio/gaudio.c
new file mode 100644
index 00000000..a83dcd85
--- /dev/null
+++ b/src/gaudio/gaudio.c
@@ -0,0 +1,318 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gaudio/gaudio.c
+ * @brief GAUDIO sub-system code.
+ *
+ * @addtogroup GAUDIO
+ * @{
+ */
+#include "gfx.h"
+
+#if GFX_USE_GAUDIO
+
+static gfxQueueGSync freeList;
+
+#if GAUDIO_NEED_PLAY
+ #include "src/gaudio/driver_play.h"
+
+ static gfxQueueASync playList;
+ static gfxSem playComplete;
+ static uint16_t playFlags;
+ #define PLAYFLG_USEEVENTS 0x0001
+ #define PLAYFLG_PLAYING 0x0002
+ #define PLAYFLG_ISINIT 0x0004
+ #if GFX_USE_GEVENT
+ static GTimer playTimer;
+ static void PlayTimerCallback(void *param);
+ #endif
+#endif
+
+#if GAUDIO_NEED_RECORD
+ #include "src/gaudio/driver_record.h"
+
+ static gfxQueueGSync recordList;
+ static uint16_t recordFlags;
+ #define RECORDFLG_USEEVENTS 0x0001
+ #define RECORDFLG_RECORDING 0x0002
+ #define RECORDFLG_STALLED 0x0004
+ #define RECORDFLG_ISINIT 0x0008
+ #if GFX_USE_GEVENT
+ static GTimer recordTimer;
+ static void RecordTimerCallback(void *param);
+ #endif
+#endif
+
+
+void _gaudioInit(void)
+{
+ gfxQueueGSyncInit(&freeList);
+ #if GAUDIO_NEED_PLAY
+ gfxQueueASyncInit(&playList);
+ #if GFX_USE_GEVENT
+ gtimerInit(&playTimer);
+ #endif
+ gfxSemInit(&playComplete, 0, 0);
+ #endif
+ #if GAUDIO_NEED_RECORD
+ gfxQueueGSyncInit(&recordList);
+ #if GFX_USE_GEVENT
+ gtimerInit(&recordTimer);
+ #endif
+ #endif
+}
+
+void _gaudioDeinit(void)
+{
+ #if GAUDIO_NEED_PLAY
+ #if GFX_USE_GEVENT
+ gtimerDeinit(&playTimer);
+ #endif
+ gfxSemDestroy(&playComplete);
+ #endif
+ #if GAUDIO_NEED_RECORD
+ #if GFX_USE_GEVENT
+ gtimerDeinit(&recordTimer);
+ #endif
+ #endif
+}
+
+bool_t gaudioAllocBuffers(unsigned num, size_t size) {
+ GAudioData *paud;
+
+ if (num < 1)
+ return FALSE;
+
+ // Round up to a multiple of 4 to prevent problems with structure alignment
+ size = (size + 3) & ~0x03;
+
+ // Allocate the memory
+ if (!(paud = gfxAlloc((size+sizeof(GAudioData)) * num)))
+ return FALSE;
+
+ // Add each of them to our free list
+ for(;num--; paud = (GAudioData *)((char *)(paud+1)+size)) {
+ paud->size = size;
+ gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
+ }
+
+ return TRUE;
+}
+
+void gaudioReleaseBuffer(GAudioData *paud) {
+ gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
+}
+
+GAudioData *gaudioGetBuffer(delaytime_t ms) {
+ return (GAudioData *)gfxQueueGSyncGet(&freeList, ms);
+}
+
+#if GAUDIO_NEED_PLAY
+
+ bool_t gaudioPlayInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format) {
+ gaudioPlayStop();
+ playFlags &= ~PLAYFLG_ISINIT;
+ if (!gaudio_play_lld_init(channel, frequency, format))
+ return FALSE;
+ playFlags |= PLAYFLG_ISINIT;
+ return TRUE;
+ }
+
+ void gaudioPlay(GAudioData *paud) {
+ if (!(playFlags & PLAYFLG_ISINIT)) {
+ // Oops - init failed - return it directly to the free-list
+ if (paud) {
+ gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
+ gfxYield(); // Make sure we get no endless cpu hogging loops
+ }
+ return;
+ }
+
+ if (paud)
+ gfxQueueASyncPut(&playList, (gfxQueueASyncItem *)paud);
+ playFlags |= PLAYFLG_PLAYING;
+ gaudio_play_lld_start();
+ }
+
+ void gaudioPlayPause(void) {
+ if ((playFlags & (PLAYFLG_ISINIT|PLAYFLG_PLAYING)) == (PLAYFLG_ISINIT|PLAYFLG_PLAYING))
+ gaudio_play_lld_stop();
+ }
+
+ void gaudioPlayStop(void) {
+ GAudioData *paud;
+
+ if (playFlags & PLAYFLG_PLAYING)
+ gaudio_play_lld_stop();
+ while((paud = (GAudioData *)gfxQueueASyncGet(&playList)))
+ gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
+ }
+
+ bool_t gaudioPlaySetVolume(uint8_t vol) {
+ return gaudio_play_lld_set_volume(vol);
+ }
+
+ bool_t gaudioPlayWait(delaytime_t ms) {
+ if (!(playFlags & PLAYFLG_PLAYING))
+ return TRUE;
+ return gfxSemWait(&playComplete, ms);
+ }
+
+ #if GFX_USE_GEVENT
+ static void PlayTimerCallback(void *param) {
+ (void) param;
+ GSourceListener *psl;
+ GEventAudioPlay *pe;
+
+ psl = 0;
+ while ((psl = geventGetSourceListener((GSourceHandle)&playTimer, psl))) {
+ if (!(pe = (GEventAudioPlay *)geventGetEventBuffer(psl))) {
+ // This listener is missing - save this.
+ psl->srcflags |= GAUDIO_PLAY_LOSTEVENT;
+ continue;
+ }
+
+ pe->type = GEVENT_AUDIO_PLAY;
+ pe->flags = psl->srcflags;
+ psl->srcflags = 0;
+ if ((playFlags & PLAYFLG_PLAYING))
+ pe->flags |= GAUDIO_PLAY_PLAYING;
+ if (!gfxQueueGSyncIsEmpty(&freeList))
+ pe->flags |= GAUDIO_PLAY_FREEBLOCK;
+ geventSendEvent(psl);
+ }
+ }
+
+ GSourceHandle gaudioPlayGetSource(void) {
+ if (!gtimerIsActive(&playTimer))
+ gtimerStart(&playTimer, PlayTimerCallback, 0, TRUE, TIME_INFINITE);
+ playFlags |= PLAYFLG_USEEVENTS;
+ return (GSourceHandle)&playTimer;
+ }
+ #endif
+
+ /**
+ * Routines provided for use by drivers.
+ */
+
+ GAudioData *gaudioPlayGetDataBlockI(void) {
+ return (GAudioData *)gfxQueueASyncGetI(&playList);
+ }
+
+ void gaudioPlayReleaseDataBlockI(GAudioData *paud) {
+ gfxQueueGSyncPutI(&freeList, (gfxQueueGSyncItem *)paud);
+ #if GFX_USE_GEVENT
+ if (playFlags & PLAYFLG_USEEVENTS)
+ gtimerJabI(&playTimer);
+ #endif
+ }
+
+ void gaudioPlayDoneI(void) {
+ playFlags &= ~PLAYFLG_PLAYING;
+ #if GFX_USE_GEVENT
+ if (playFlags & PLAYFLG_USEEVENTS)
+ gtimerJabI(&playTimer);
+ #endif
+ gfxSemSignalI(&playComplete); // This should really be gfxSemSignalAllI(&playComplete);
+ }
+#endif
+
+#if GAUDIO_NEED_RECORD
+ bool_t gaudioRecordInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format) {
+ gaudioRecordStop();
+ recordFlags &= ~RECORDFLG_ISINIT;
+ if (!gaudio_record_lld_init(channel, frequency, format))
+ return FALSE;
+ recordFlags |= RECORDFLG_ISINIT;
+ return TRUE;
+ }
+
+ void gaudioRecordStart(void) {
+ if (!(recordFlags & RECORDFLG_ISINIT))
+ return; // Oops - init failed
+
+ recordFlags |= RECORDFLG_RECORDING;
+ recordFlags &= ~RECORDFLG_STALLED;
+ gaudio_record_lld_start();
+ }
+
+ void gaudioRecordStop(void) {
+ GAudioData *paud;
+
+ if ((recordFlags & (RECORDFLG_RECORDING|RECORDFLG_STALLED)) == RECORDFLG_RECORDING)
+ gaudio_record_lld_stop();
+ recordFlags &= ~(RECORDFLG_RECORDING|RECORDFLG_STALLED);
+ while((paud = (GAudioData *)gfxQueueGSyncGet(&recordList, TIME_IMMEDIATE)))
+ gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
+ }
+
+ GAudioData *gaudioRecordGetData(delaytime_t ms) {
+ return (GAudioData *)gfxQueueGSyncGet(&recordList, ms);
+ }
+
+ #if GFX_USE_GEVENT
+ static void RecordTimerCallback(void *param) {
+ (void) param;
+ GSourceListener *psl;
+ GEventAudioRecord *pe;
+
+ psl = 0;
+ while ((psl = geventGetSourceListener((GSourceHandle)&recordTimer, psl))) {
+ if (!(pe = (GEventAudioRecord *)geventGetEventBuffer(psl))) {
+ // This listener is missing - save this.
+ psl->srcflags |= GAUDIO_RECORD_LOSTEVENT;
+ continue;
+ }
+ pe->type = GEVENT_AUDIO_RECORD;
+ pe->flags = psl->srcflags;
+ psl->srcflags = 0;
+ if ((recordFlags & RECORDFLG_RECORDING))
+ pe->flags |= GAUDIO_RECORD_RECORDING;
+ if ((recordFlags & RECORDFLG_STALLED))
+ pe->flags |= GAUDIO_RECORD_STALL;
+ if (!gfxQueueGSyncIsEmpty(&recordList))
+ pe->flags |= GAUDIO_RECORD_GOTBLOCK;
+ geventSendEvent(psl);
+ }
+ }
+
+ GSourceHandle gaudioRecordGetSource(void) {
+ if (!gtimerIsActive(&recordTimer))
+ gtimerStart(&recordTimer, RecordTimerCallback, 0, TRUE, TIME_INFINITE);
+ recordFlags |= RECORDFLG_USEEVENTS;
+ return (GSourceHandle)&recordTimer;
+ }
+ #endif
+
+ /**
+ * Routines provided for use by drivers.
+ */
+
+ GAudioData *gaudioRecordGetFreeBlockI(void) {
+ return (GAudioData *)gfxQueueGSyncGetI(&freeList);
+ }
+
+ void gaudioRecordSaveDataBlockI(GAudioData *paud) {
+ gfxQueueGSyncPutI(&recordList, (gfxQueueGSyncItem *)paud);
+ #if GFX_USE_GEVENT
+ if (recordFlags & RECORDFLG_USEEVENTS)
+ gtimerJabI(&recordTimer);
+ #endif
+ }
+
+ void gaudioRecordDoneI(void) {
+ recordFlags |= RECORDFLG_STALLED;
+ #if GFX_USE_GEVENT
+ if (recordFlags & RECORDFLG_USEEVENTS)
+ gtimerJabI(&recordTimer);
+ #endif
+ }
+#endif
+
+#endif /* GFX_USE_GAUDIO */
+/** @} */
diff --git a/src/gaudio/sys_defs.h b/src/gaudio/sys_defs.h
new file mode 100644
index 00000000..a9a951b7
--- /dev/null
+++ b/src/gaudio/sys_defs.h
@@ -0,0 +1,340 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gaudio/sys_defs.h
+ *
+ * @addtogroup GAUDIO
+ *
+ * @brief Module to handle audio recording and play-back
+ *
+ * @{
+ */
+
+#ifndef _GAUDIO_H
+#define _GAUDIO_H
+
+#include "gfx.h"
+
+#if GFX_USE_GAUDIO || defined(__DOXYGEN__)
+
+/* Include the driver defines */
+#if GAUDIO_NEED_PLAY
+ #include "gaudio_play_config.h"
+#endif
+#if GAUDIO_NEED_RECORD
+ #include "gaudio_record_config.h"
+#endif
+
+/*===========================================================================*/
+/* Type definitions */
+/*===========================================================================*/
+
+/**
+ * @brief Contains Audio Data Samples
+ * @note This structure is followed immediately by the sample data itself.
+ * When allocating the buffers for the sample data put this structure
+ * at the beginning of the buffer.
+ */
+typedef struct GAudioData {
+ gfxQueueASyncItem next; // @< Used for queuing the buffers
+ size_t size; // @< The size of the buffer area following this structure (in bytes)
+ size_t len; // @< The length of the data in the buffer area (in bytes)
+} GAudioData;
+
+
+// Event types for GAUDIO
+#define GEVENT_AUDIO_PLAY (GEVENT_GAUDIO_FIRST+0)
+#define GEVENT_AUDIO_RECORD (GEVENT_GAUDIO_FIRST+1)
+
+#if GFX_USE_GEVENT || defined(__DOXYGEN__)
+ /**
+ * @brief The Audio play event structure.
+ * @{
+ */
+ typedef struct GEventAudioPlay_t {
+ /**
+ * @brief The type of this event (GEVENT_AUDIO_PLAY)
+ */
+ GEventType type;
+ /**
+ * @brief The event flags
+ */
+ uint16_t flags;
+ /**
+ * @brief The event flag values.
+ * @{
+ */
+ #define GAUDIO_PLAY_LOSTEVENT 0x0001 /**< @brief The last GEVENT_AUDIO_PLAY event was lost */
+ #define GAUDIO_PLAY_PLAYING 0x0002 /**< @brief The audio out system is currently playing */
+ #define GAUDIO_PLAY_FREEBLOCK 0x0004 /**< @brief An audio buffer has been freed */
+ /** @} */
+ } GEventAudioPlay;
+ /** @} */
+
+ /**
+ * @brief The Audio record event structure.
+ * @{
+ */
+ typedef struct GEventAudioRecord_t {
+ /**
+ * @brief The type of this event (GEVENT_AUDIO_RECORD)
+ */
+ GEventType type;
+ /**
+ * @brief The event flags
+ */
+ uint16_t flags;
+ /**
+ * @brief The event flag values.
+ * @{
+ */
+ #define GAUDIO_RECORD_LOSTEVENT 0x0001 /**< @brief The last GEVENT_AUDIO_IN event was lost */
+ #define GAUDIO_RECORD_RECORDING 0x0002 /**< @brief The audio recording system is currently recording */
+ #define GAUDIO_RECORD_GOTBLOCK 0x0004 /**< @brief An audio buffer is ready for processing */
+ #define GAUDIO_RECORD_STALL 0x0008 /**< @brief The recording process has stalled due to no free buffers */
+ /** @} */
+ } GEventAudioRecord;
+ /** @} */
+#endif
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Allocate some audio buffers and put them on the free list
+ * @return TRUE is it succeeded. FALSE on allocation failure.
+ *
+ * @param[in] num The number of buffers to allocate
+ * @param[in] size The size (in bytes) of each buffer
+ *
+ * @api
+ */
+bool_t gaudioAllocBuffers(unsigned num, size_t size);
+
+/**
+ * @brief Get an audio buffer from the free list
+ * @return A GAudioData pointer or NULL if the timeout is exceeded
+ *
+ * @params[in] ms The maximum amount of time in milliseconds to wait for a buffer if one is not available.
+ *
+ * @api
+ */
+GAudioData *gaudioGetBuffer(delaytime_t ms);
+
+/**
+ * @brief Release a buffer back to the free list
+ *
+ * @param[in] paud The buffer to put (back) on the free-list.
+ *
+ * @note This call should be used to return any buffers that were taken from
+ * the free-list once they have been finished with. It can also be used
+ * to put new buffers onto the free-list. Just make sure the "size" field
+ * of the GAudioData structure has been filled in first.
+ *
+ * @api
+ */
+void gaudioReleaseBuffer(GAudioData *paud);
+
+#if GAUDIO_NEED_PLAY || defined(__DOXYGEN__)
+ /**
+ * @brief Set the audio device to play on the specified channel and with the specified
+ * sample frequency.
+ * @return TRUE is successful, FALSE if the driver doesn't accept those parameters.
+ *
+ * @param[in] channel The audio output channel to use. Can be set from 0 to GAUDIO_PLAY_NUM_CHANNELS - 1
+ * @param[in] frequency The audio sample rate in samples per second
+ * @param[in] format The audio sample format
+ *
+ * @note Some channels are mono, and some are stereo. See your driver config file
+ * to determine which channels to use and whether they are stereo or not.
+ * @note Only one channel can be playing at a time. Calling this will stop any
+ * currently playing channel.
+ *
+ * @api
+ */
+ bool_t gaudioPlayInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format);
+
+ /**
+ * @brief Play the specified sample data.
+ * @details The sample data is output to the audio channel. On completion the buffer is returned to the free-list.
+ * @pre @p gaudioPlayInit must have been called first to set the channel and sample frequency.
+ *
+ * @param[in] paud The audio sample buffer to play. It can be NULL (used to restart paused audio)
+ *
+ * @note Calling this will cancel any pause.
+ * @note Before calling this function the len field of the GAudioData structure must be
+ * specified (in bytes).
+ * @note For stereo channels the sample data is interleaved in the buffer.
+ * @note This call returns before the data has completed playing. Subject to available buffers (which
+ * can be obtained from the free-list), any number of buffers may be played. They will be queued
+ * for playing in the order they are supplied to this routine and played when previous buffers are
+ * complete. In this way continuous playing can be obtained without audio gaps.
+ *
+ * @api
+ */
+ void gaudioPlay(GAudioData *paud);
+
+ /**
+ * @brief Pause any currently playing sounds.
+ *
+ * @note If nothing is currently playing this routine does nothing. To restart playing call @p gaudioPlay()
+ * with or without a new sample buffer.
+ * @note Some drivers will not respond until a buffer boundary.
+ *
+ * @api
+ */
+ void gaudioPlayPause(void);
+
+ /**
+ * @brief Stop any currently playing sounds.
+ *
+ * @note This stops any playing sounds and returns any currently queued buffers back to the free-list.
+ * @note Some drivers will not respond until a buffer boundary.
+ *
+ * @api
+ */
+ void gaudioPlayStop(void);
+
+ /**
+ * @brief Set the output volume.
+ * @return TRUE if successful.
+ *
+ * @param[in] 0->255 (0 = muted)
+ *
+ * @note Some drivers may not support this. They will return FALSE.
+ * @note For stereo devices, both channels are set to the same volume.
+ *
+ * @api
+ */
+ bool_t gaudioPlaySetVolume(uint8_t vol);
+
+ #if GFX_USE_GEVENT || defined(__DOXYGEN__)
+ /**
+ * @brief Turn on sending results to the GEVENT sub-system.
+ * @details Returns a GSourceHandle to listen for GEVENT_AUDIO_OUT events.
+ *
+ * @note The audio output will not use the GEVENT system unless this is
+ * called first. This saves processing time if the application does
+ * not want to use the GEVENT sub-system for audio output.
+ * Once turned on it can only be turned off by calling @p gaudioPlayInit() again.
+ * @note The audio output is capable of signaling via this method and other methods
+ * at the same time.
+ *
+ * @return The GSourceHandle
+ *
+ * @api
+ */
+ GSourceHandle gaudioPlayGetSource(void);
+ #endif
+
+ /**
+ * @brief Wait for any currently playing sounds to complete
+ * @return TRUE if there is now nothing playing or FALSE if the timeout is exceeded
+ *
+ * @params[in] ms The maximum amount of time in milliseconds to wait for playing to complete.
+ *
+ * @api
+ */
+ bool_t gaudioPlayWait(delaytime_t ms);
+#endif
+
+#if GAUDIO_NEED_RECORD || defined(__DOXYGEN__)
+ /**
+ * @brief Initialise (but not start) the Audio Recording sub-system.
+ * @details Returns FALSE for an invalid channel or other invalid parameter.
+ *
+ * @param[in] channel The channel to convert. Can be set from 0 to GAUDIO_RECORD_NUM_CHANNELS - 1
+ * @param[in] frequency The sample frequency
+ * @param[in] format The audio sample format requested
+ *
+ * @note Only one channel is active at a time. If an audio input is running it will be stopped.
+ * The Event subsystem is disconnected from the audio subsystem and any binary semaphore
+ * event is forgotten.
+ * @note Some channels may be stereo channels which return twice as much sample data with
+ * the left and right channel data interleaved. Other channels may be mono channels.
+ * Where stereo channels exist the low level driver may also
+ * offer the left and right channels separately.
+ * @note Due to a bug in Chibi-OS each buffer on the free-list must contain an even number of
+ * samples and for stereo devices it must hold a number of samples that is evenly divisible by 4.
+ * This requirement applies only to ChibiOS where the audio driver uses
+ * a ChibiOS hal driver like the cpu ADC driver. This applies even it is used indirectly via
+ * the uGFX GADC driver.
+ * @note The number of samples for stereo devices will be double the number of conversions.
+ * Make sure you allocate your buffers large enough. Each channel is then interleaved
+ * into the provided buffer.
+ *
+ * @return FALSE if invalid channel or parameter
+ *
+ * @api
+ */
+ bool_t gaudioRecordInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format);
+
+ /**
+ * @brief Start the audio recording.
+ * @pre It must have been initialised first with @p gaudioRecordInit()
+ *
+ * @api
+ */
+ void gaudioRecordStart(void);
+
+ /**
+ * @brief Stop the audio recording.
+ *
+ * @note All audio recording data that has not yet been retrieved is automatically
+ * returned to the free-list.
+ * @api
+ */
+ void gaudioRecordStop(void);
+
+ /**
+ * @brief Get a filled audio buffer from the recording list
+ * @return A GAudioData pointer or NULL if the timeout is exceeded
+ *
+ * @params[in] ms The maximum amount of time in milliseconds to wait for data if some is not currently available.
+ *
+ * @note After processing the audio data, your application must return the buffer to the free-list so that
+ * it can be used to record more audio into. This can be done via the play list using @p gaudioPlay() or
+ * directly using @p gaudioReleaseBuffer().
+ * @api
+ */
+ GAudioData *gaudioRecordGetData(delaytime_t ms);
+
+ #if GFX_USE_GEVENT || defined(__DOXYGEN__)
+ /**
+ * @brief Turn on sending results to the GEVENT sub-system.
+ * @details Returns a GSourceHandle to listen for GEVENT_AUDIO_RECORD events.
+ *
+ * @note Audio recording will not use the GEVENT system unless this is
+ * called first. This saves processing time if the application does
+ * not want to use the GEVENT sub-system for audio recording.
+ * Once turned on it can only be turned off by calling @p gaudioRecordInit() again.
+ * @note The audio input is capable of signaling via this and other methods
+ * at the same time.
+ *
+ * @return The GSourceHandle
+ *
+ * @api
+ */
+ GSourceHandle gaudioRecordGetSource(void);
+ #endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_USE_GAUDIO */
+
+#endif /* _GAUDIO_H */
+/** @} */
+
diff --git a/src/gaudio/sys_make.mk b/src/gaudio/sys_make.mk
new file mode 100644
index 00000000..438892c0
--- /dev/null
+++ b/src/gaudio/sys_make.mk
@@ -0,0 +1 @@
+GFXSRC += $(GFXLIB)/src/gaudio/gaudio.c
diff --git a/src/gaudio/sys_options.h b/src/gaudio/sys_options.h
new file mode 100644
index 00000000..1363d703
--- /dev/null
+++ b/src/gaudio/sys_options.h
@@ -0,0 +1,44 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gaudio/sys_options.h
+ * @brief GAUDIO - Audio subsystem options header file.
+ *
+ * @addtogroup GAUDIO
+ * @{
+ */
+
+#ifndef _GAUDIO_OPTIONS_H
+#define _GAUDOUT_OPTIONS_H
+
+/**
+ * @name GAUDIO Functionality to be included
+ * @{
+ */
+ /**
+ * @brief Audio Play capability is needed
+ */
+ #ifndef GAUDIO_NEED_PLAY
+ #define GAUDIO_NEED_PLAY FALSE
+ #endif
+ /**
+ * @brief Audio Recording capability is needed
+ */
+ #ifndef GAUDIO_NEED_RECORD
+ #define GAUDIO_NEED_RECORD FALSE
+ #endif
+/**
+ * @}
+ *
+ * @name GAUDIO Optional Sizing Parameters
+ * @{
+ */
+/** @} */
+
+#endif /* _GAUDIO_OPTIONS_H */
+/** @} */
diff --git a/src/gaudio/sys_rules.h b/src/gaudio/sys_rules.h
new file mode 100644
index 00000000..a3f0dffc
--- /dev/null
+++ b/src/gaudio/sys_rules.h
@@ -0,0 +1,54 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gaudio/sys_rules.h
+ * @brief GAUDIO safety rules header file.
+ *
+ * @addtogroup GAUDIO
+ * @{
+ */
+
+#ifndef _GAUDIO_RULES_H
+#define _GAUDIO_RULES_H
+
+#if GFX_USE_GAUDIO
+ #if !GAUDIO_NEED_PLAY && !GAUDIO_NEED_RECORD
+ #error "GAUDIO: GAUDIO_NEED_PLAY and/or GAUDIO_NEED_RECORD is required if GFX_USE_GAUDIO is TRUE"
+ #endif
+ #if !GFX_USE_GQUEUE
+ #if GFX_DISPLAY_RULE_WARNINGS
+ #warning "GAUDIO: GFX_USE_GQUEUE is required if GFX_USE_GAUDIO is TRUE. It has been turned on for you."
+ #endif
+ #undef GFX_USE_GQUEUE
+ #define GFX_USE_GQUEUE TRUE
+ #endif
+ #if !GQUEUE_NEED_ASYNC
+ #if GFX_DISPLAY_RULE_WARNINGS
+ #warning "GAUDIO: GQUEUE_NEED_ASYNC is required if GFX_USE_GAUDIO is TRUE. It has been turned on for you."
+ #endif
+ #undef GQUEUE_NEED_ASYNC
+ #define GQUEUE_NEED_ASYNC TRUE
+ #endif
+ #if !GQUEUE_NEED_GSYNC
+ #if GFX_DISPLAY_RULE_WARNINGS
+ #warning "GAUDIO: GQUEUE_NEED_GSYNC is required if GFX_USE_GAUDIO is TRUE. It has been turned on for you."
+ #endif
+ #undef GQUEUE_NEED_GSYNC
+ #define GQUEUE_NEED_GSYNC TRUE
+ #endif
+ #if GFX_USE_GEVENT && !GFX_USE_GTIMER
+ #if GFX_DISPLAY_RULE_WARNINGS
+ #warning "GAUDIO: GFX_USE_GTIMER is required if GFX_USE_GAUDIO and GFX_USE_GEVENT are TRUE. It has been turned on for you."
+ #endif
+ #undef GFX_USE_GTIMER
+ #define GFX_USE_GTIMER TRUE
+ #endif
+#endif
+
+#endif /* _GAUDIO_RULES_H */
+/** @} */