First GAUDOUT work. Still incomplete but compiling.

Also moved drivers/gaudin to drivers/audio as most audio codecs support input and output in a single device.
This commit is contained in:
inmarket 2014-02-27 08:04:54 +10:00
parent f9495a75e1
commit a56e4ac7dc
24 changed files with 707 additions and 81 deletions

View File

@ -5,4 +5,4 @@ include $(GFXLIB)/drivers/gdisp/Nokia6610GE12/gdisp_lld.mk
include $(GFXLIB)/drivers/gadc/AT91SAM7/gadc_lld.mk
include $(GFXLIB)/drivers/ginput/dial/GADC/ginput_lld.mk
include $(GFXLIB)/drivers/ginput/toggle/Pal/ginput_lld.mk
include $(GFXLIB)/drivers/gaudin/gadc/gaudin_lld.mk
include $(GFXLIB)/drivers/audio/gadc/driver.mk

View File

@ -5,4 +5,4 @@ include $(GFXLIB)/drivers/gdisp/Nokia6610GE8/gdisp_lld.mk
include $(GFXLIB)/drivers/gadc/AT91SAM7/gadc_lld.mk
include $(GFXLIB)/drivers/ginput/dial/GADC/ginput_lld.mk
include $(GFXLIB)/drivers/ginput/toggle/Pal/ginput_lld.mk
include $(GFXLIB)/drivers/gaudin/gadc/gaudin_lld.mk
include $(GFXLIB)/drivers/audio/gadc/driver.mk

View File

@ -1,5 +1,5 @@
GFXINC += $(GFXLIB)/boards/base/Win32
GFXSRC +=
include $(GFXLIB)/drivers/multiple/Win32/gdisp_lld.mk
include $(GFXLIB)/drivers/gaudin/Win32/gaudin_lld.mk
include $(GFXLIB)/drivers/multiple/Win32/driver.mk
include $(GFXLIB)/drivers/audio/Win32/driver.mk

View File

@ -0,0 +1,6 @@
# List the required driver.
GFXSRC += $(GFXLIB)/drivers/audio/Win32/gaudin_lld.c \
$(GFXLIB)/drivers/audio/Win32/gaudout_lld.c
# Required include directories
GFXINC += $(GFXLIB)/drivers/audio/Win32

View File

@ -6,7 +6,7 @@
*/
/**
* @file drivers/gaudin/Win32/gaudin_lld.c
* @file drivers/audio/Win32/gaudin_lld.c
* @brief GAUDIN - Driver file for Win32.
*/
@ -26,9 +26,10 @@
#include <mmsystem.h>
static HWAVEIN ah;
static volatile int nUsedBuffers;
static volatile int nQueuedBuffers;
static bool_t isClosing;
static WAVEHDR *pWaveHdrs;
static HANDLE waveInThread;
static HANDLE waveThread;
static DWORD threadID;
/*
@ -45,7 +46,7 @@ static void PrintWaveErrorMsg(DWORD err, TCHAR * str)
}
*/
/**************************** waveInProc() *******************************
/**************************** waveProc() *******************************
* We don't use CALLBACK_FUNCTION because it is restricted to calling only
* a few particular Windows functions, namely some of the time functions,
* and a few of the Low Level MIDI API. If you violate this rule, your app can
@ -55,43 +56,32 @@ static void PrintWaveErrorMsg(DWORD err, TCHAR * str)
* anyway, so instead just use CALLBACK_THREAD here instead.
*************************************************************************/
DWORD WINAPI waveInProc(LPVOID arg) {
static DWORD WINAPI waveProc(LPVOID arg) {
MSG msg;
bool_t isRecording;
(void) arg;
isRecording = FALSE;
while (GetMessage(&msg, 0, 0, 0)) {
switch (msg.message) {
case MM_WIM_DATA:
GAUDIN_ISR_CompleteI((audin_sample_t *)((WAVEHDR *)msg.lParam)->lpData, ((WAVEHDR *)msg.lParam)->dwBytesRecorded/sizeof(audin_sample_t));
/* Are we still recording? */
if (isRecording) {
/* Yes. Now we need to requeue this buffer so the driver can use it for another block of audio
/* Are we closing? */
if (isClosing) {
/* Yes. We aren't recording, so another WAVEHDR has been returned to us after recording has stopped.
* When we get all of them back, things can be cleaned up
*/
nQueuedBuffers--;
waveInUnprepareHeader(ah, (WAVEHDR *)msg.lParam, sizeof(WAVEHDR));
} else {
/* No. Now we need to requeue this buffer so the driver can use it for another block of audio
* data. NOTE: We shouldn't need to waveInPrepareHeader() a WAVEHDR that has already been prepared once.
* Note: We are assuming here that both the application can still access the buffer while
* it is on the queue.
*/
waveInAddBuffer(ah, (WAVEHDR *)msg.lParam, sizeof(WAVEHDR));
} else {
/* We aren't recording, so another WAVEHDR has been returned to us after recording has stopped.
* When we get all of them back, DoneAll will be equal to how many WAVEHDRs we queued
*/
nUsedBuffers--;
waveInUnprepareHeader(ah, (WAVEHDR *)msg.lParam, sizeof(WAVEHDR));
}
break;
case MM_WIM_OPEN:
isRecording = TRUE;
break;
case MM_WIM_CLOSE:
isRecording = FALSE;
break;
}
}
return 0;
@ -101,12 +91,20 @@ DWORD WINAPI waveInProc(LPVOID arg) {
/* External declarations. */
/*===========================================================================*/
void gaudin_lld_deinit(void) {
if (ah) {
isClosing = TRUE;
waveInReset(ah);
while(nQueuedBuffers) Sleep(1);
waveInClose(ah);
ah = 0;
if (pWaveHdrs) gfxFree(pWaveHdrs);
pWaveHdrs = 0;
isClosing = FALSE;
}
}
void gaudin_lld_init(const gaudin_params *paud) {
// uint16_t channel;
// uint32_t frequency;
// audin_sample_t *buffer;
// size_t bufcount;
// size_t samplesPerEvent;
WAVEFORMATEX wfx;
size_t spaceleft;
audin_sample_t *p;
@ -114,15 +112,15 @@ void gaudin_lld_init(const gaudin_params *paud) {
size_t nBuffers;
size_t sz;
if (!waveInThread) {
if (!(waveInThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)waveInProc, 0, 0, &threadID))) {
fprintf(stderr, "GAUDIN: Can't create WAVE recording thread\n");
if (!waveThread) {
if (!(waveThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)waveProc, 0, 0, &threadID))) {
fprintf(stderr, "GAUDIN/GAUDOUT: Can't create WAVE recording thread\n");
return;
}
CloseHandle(waveInThread);
CloseHandle(waveThread);
}
nUsedBuffers = 0;
gaudin_lld_deinit();
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = paud->channel == GAUDIN_STEREO ? 2 : 1;
@ -153,26 +151,20 @@ void gaudin_lld_init(const gaudin_params *paud) {
phdr->dwFlags = 0;
if (!waveInPrepareHeader(ah, phdr, sizeof(WAVEHDR))
&& !waveInAddBuffer(ah, phdr, sizeof(WAVEHDR)))
nUsedBuffers++;
nQueuedBuffers++;
else
fprintf(stderr, "GAUDIN: Buffer prepare failed\n");
}
if (!nUsedBuffers)
if (!nQueuedBuffers)
fprintf(stderr, "GAUDIN: Failed to prepare any buffers\n");
}
void gadc_lld_start(void) {
if (nUsedBuffers)
waveInStart(ah);
void gaudin_lld_start(void) {
waveInStart(ah);
}
void gadc_lld_stop(void) {
waveInReset(ah);
while(nUsedBuffers) Sleep(1);
if (pWaveHdrs) {
gfxFree(pWaveHdrs);
pWaveHdrs = 0;
}
void gaudin_lld_stop(void) {
waveInStop(ah);
}
#endif /* GFX_USE_GAUDIN */

View File

@ -6,7 +6,7 @@
*/
/**
* @file drivers/multiple/Win32/gaudin_lld_config.h
* @file drivers/audio/Win32/gaudin_lld_config.h
* @brief GAUDIN Driver config file.
*
* @addtogroup GAUDIN

View File

@ -0,0 +1,190 @@
/*
* 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 drivers/audio/Win32/gaudout_lld.c
* @brief GAUDOUT - Driver file for Win32.
*/
#include "gfx.h"
#if GFX_USE_GAUDOUT
/* Include the driver defines */
#include "src/gaudout/driver.h"
#undef Red
#undef Green
#undef Blue
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <mmsystem.h>
#define MAX_WAVE_HEADERS 2 // Larger numbers enable more buffering which is good for ensuring
// there are no skips due to data not being available, however larger
// numbers of buffers also create higher latency.
static HWAVEOUT ah = 0;
static volatile int nQueuedBuffers;
static bool_t isRunning;
static WAVEHDR WaveHdrs[MAX_WAVE_HEADERS];
static HANDLE waveThread;
static DWORD threadID;
/*
static void PrintWaveErrorMsg(DWORD err, TCHAR * str)
{
#define BUFFERSIZE 128
char buffer[BUFFERSIZE];
fprintf(stderr, "GAUDOUT: ERROR 0x%08X: %s\r\n", err, str);
if (mciGetErrorString(err, &buffer[0], sizeof(buffer)))
fprintf(stderr, "%s\r\n", &buffer[0]);
else
fprintf(stderr, "0x%08X returned!\r\n", err);
}
*/
/**************************** waveProc() *******************************
* We don't use CALLBACK_FUNCTION because it is restricted to calling only
* a few particular Windows functions, namely some of the time functions,
* and a few of the Low Level MIDI API. If you violate this rule, your app can
* hang inside of the callback). One of the Windows API that a callback can't
* call is waveOutUnPrepareBuffer() which is what we need to use whenever we receive a
* MM_WOM_DONE. My callback would need to defer that job to another thread
* anyway, so instead just use CALLBACK_THREAD here instead.
*************************************************************************/
static bool_t senddata(WAVEHDR *pwh) {
GAudioData *paud;
// Get the next data block to send
if (!(paud = gaudoutGetDataBlockI()))
return FALSE;
// Prepare the wave header for Windows
pwh->dwUser = (DWORD_PTR)paud;
pwh->lpData = (LPSTR)(paud+1); // The data is on the end of the structure
pwh->dwBufferLength = paud->len;
pwh->dwFlags = 0;
pwh->dwLoops = 0;
if (waveOutPrepareHeader(ah, pwh, sizeof(WAVEHDR))) {
pwh->lpData = 0;
fprintf(stderr, "GAUDOUT: Failed to prepare a buffer");
return FALSE;
}
// Send it to windows
if (waveOutWrite(ah, pwh, sizeof(WAVEHDR))) {
pwh->lpData = 0;
fprintf(stderr, "GAUDOUT: Failed to write the buffer");
return FALSE;
}
nQueuedBuffers++;
return TRUE;
}
static DWORD WINAPI waveProc(LPVOID arg) {
MSG msg;
WAVEHDR *pwh;
(void) arg;
while (GetMessage(&msg, 0, 0, 0)) {
switch (msg.message) {
case MM_WOM_DONE:
pwh = (WAVEHDR *)msg.lParam;
// Windows - Let go!
waveOutUnprepareHeader(ah, pwh, sizeof(WAVEHDR));
// Give the buffer back to the Audio Free List
gaudoutReleaseDataBlockI((GAudioData *)pwh->dwUser);
pwh->lpData = 0;
nQueuedBuffers--;
// Try and get a new block
if (isRunning)
senddata(pwh);
break;
}
}
return 0;
}
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
void gaudout_lld_deinit() {
if (ah) {
isRunning = FALSE;
waveOutReset(ah);
while(nQueuedBuffers) Sleep(1);
waveOutClose(ah);
ah = 0;
}
}
bool_t gaudout_lld_init(uint16_t channel, uint32_t frequency) {
WAVEFORMATEX wfx;
if (!waveThread) {
if (!(waveThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)waveProc, 0, 0, &threadID))) {
fprintf(stderr, "GAUDOUT: Can't create WAVE play-back thread\n");
return FALSE;
}
CloseHandle(waveThread);
}
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = channel == GAUDOUT_STEREO ? 2 : 1;
wfx.nSamplesPerSec = frequency;
wfx.nBlockAlign = wfx.nChannels * sizeof(audout_sample_t);
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.wBitsPerSample = sizeof(audout_sample_t) * 8;
wfx.cbSize = 0;
if (waveOutOpen(&ah, WAVE_MAPPER, &wfx, (DWORD_PTR)threadID, 0, CALLBACK_THREAD)) {
fprintf(stderr, "GAUDOUT: Can't open WAVE play-back device\n");
return FALSE;
}
return TRUE;
}
bool_t gaudout_lld_set_volume(uint8_t vol) {
if (!ah)
return FALSE;
return waveOutSetVolume(ah, (((uint16_t)vol)<<8)|vol) != 0;
}
void gaudout_lld_start(void) {
WAVEHDR *pwh;
if (!ah)
return;
isRunning = TRUE;
while (nQueuedBuffers < MAX_WAVE_HEADERS) {
// Find the empty one - there will always be at least one.
for(pwh = WaveHdrs; pwh->lpData; pwh++);
// Grab the next audio block from the Audio Out Queue
if (!senddata(pwh))
break;
}
}
void gaudout_lld_stop(void) {
isRunning = FALSE;
if (ah)
waveOutReset(ah);
}
#endif /* GFX_USE_GAUDOUT */

View File

@ -0,0 +1,64 @@
/*
* 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 drivers/audio/Win32/gaudout_lld_config.h
* @brief GAUDOUT Driver config file.
*
* @addtogroup GAUDOUT
* @{
*/
#ifndef GAUDOUT_LLD_CONFIG_H
#define GAUDIN_LLD_CONFIG_H
#if GFX_USE_GAUDOUT
/*===========================================================================*/
/* Driver hardware support. */
/*===========================================================================*/
/**
* @brief The audio input sample type
*/
//typedef uint8_t audout_sample_t;
typedef int16_t audout_sample_t;
/**
* @brief The maximum sample frequency supported by this audio device
*/
#define GAUDOUT_MAX_SAMPLE_FREQUENCY 44100
/**
* @brief The number of bits in a sample
*/
//#define GAUDOUT_BITS_PER_SAMPLE 8
#define GAUDOUT_BITS_PER_SAMPLE 16
/**
* @brief The format of an audio sample
*/
//#define GAUDOUT_SAMPLE_FORMAT ARRAY_DATA_8BITUNSIGNED
#define GAUDOUT_SAMPLE_FORMAT ARRAY_DATA_16BITSIGNED
/**
* @brief The number of audio channels supported by this driver
*/
#define GAUDOUT_NUM_CHANNELS 2
/**
* @brief The list of audio channels and their uses
* @{
*/
#define GAUDOUT_MONO 0
#define GAUDOUT_STEREO 1
/** @} */
#endif /* GFX_USE_GAUDOUT */
#endif /* GAUDOUT_LLD_CONFIG_H */
/** @} */

View File

@ -0,0 +1,10 @@
This driver uses the Win32 audio system to provide GAUDIN and GAUDOUT channels.
For GAUDIN - It supports 2 channels, Channel 0 being a mono channel and Channel 1 being a stereo channel.
For GAUDOUT - It supports 2 channels, Channel 0 being a mono channel and Channel 1 being a stereo channel.
For stereo, the samples are interleaved. Remember to allocate enough space for two samples per
sample period.
This is a simple driver that makes no changes to the mixer so set up the audio mixer using
the windows control panel audio mixer before starting recording/playback.

View File

@ -2,4 +2,4 @@
GFXSRC += $(GFXLIB)/drivers/gaudin/gadc/gaudin_lld.c
# Required include directories
GFXINC += $(GFXLIB)/drivers/gaudin/gadc
GFXINC += $(GFXLIB)/drivers/audio/gadc

View File

@ -50,11 +50,11 @@ void gaudin_lld_init(const gaudin_params *paud) {
*/
}
void gadc_lld_start(void) {
void gaudin_lld_start(void) {
gadcHighSpeedStart();
}
void gadc_lld_stop(void) {
void gaudin_lld_stop(void) {
gadcHighSpeedStop();
}

View File

@ -0,0 +1,6 @@
This driver uses the generic GADC driver to provide a GAUDIN device.
It supports whatever high speed device channels that your GADC driver and board supports.
For stereo, the samples are interleaved. Remember to allocate enough space for two samples per
sample period.

View File

@ -1,5 +0,0 @@
# List the required driver.
GFXSRC += $(GFXLIB)/drivers/gaudin/Win32/gaudin_lld.c
# Required include directories
GFXINC += $(GFXLIB)/drivers/gaudin/Win32

View File

@ -1,9 +0,0 @@
This driver uses the Win32 audio system to provide a GAUDIN channel.
It supports 2 channels, Channel 0 being a mono channel and Channel 1 being a stereo channel.
For stereo, the samples are interleaved. Remember to allocate enough space for two samples per
sample period.
This is a simple driver that makes no changes to the mixer so set up the audio mixer using
the windows control panel audio mixer (obviously in record mode) before starting recording.

View File

@ -1,2 +1,2 @@
GFXINC += $(GFXLIB)/drivers/multiple/Win32
GFXSRC += $(GFXLIB)/drivers/multiple/Win32/gdisp_lld_Win32.c
GFXINC += $(GFXLIB)/drivers/multiple/Win32
GFXSRC += $(GFXLIB)/drivers/multiple/Win32/gdisp_lld_Win32.c

View File

@ -83,14 +83,14 @@ void gaudin_lld_init(const gaudin_params *paud);
*
* @api
*/
void gadc_lld_start(void);
void gaudin_lld_start(void);
/**
* @brief Stop the audio input sampling
*
* @api
*/
void gadc_lld_stop(void);
void gaudin_lld_stop(void);
#ifdef __cplusplus
}

View File

@ -40,7 +40,7 @@ static uint16_t audFlags;
while ((psl = geventGetSourceListener((GSourceHandle)(&aud), psl))) {
if (!(pe = (GEventAudioIn *)geventGetEventBuffer(psl))) {
// This listener is missing - save this.
psl->srcflags |= GADC_AUDIO_IN_LOSTEVENT;
psl->srcflags |= GAUDIN_LOSTEVENT;
continue;
}
@ -107,7 +107,7 @@ bool_t gaudinInit(uint16_t channel, uint32_t frequency, audin_sample_t *buffer,
/* Stop any existing transfers */
if ((audFlags & AUDFLG_RUNNING))
gadc_lld_stop();
gaudin_lld_stop();
audFlags = 0;
/* Initialise everything */
@ -143,13 +143,13 @@ void gaudinSetBSem(gfxSem *pbsem, GEventAudioIn *pEvent) {
void gaudinStart(void) {
if (!(audFlags & AUDFLG_RUNNING)) {
audFlags |= AUDFLG_RUNNING;
gadc_lld_start();
gaudin_lld_start();
}
}
void gaudinStop(void) {
if ((audFlags & AUDFLG_RUNNING)) {
gadc_lld_stop();
gaudin_lld_stop();
audFlags &= ~AUDFLG_RUNNING;
}
}

View File

@ -55,7 +55,7 @@ typedef struct GEventAudioIn_t {
* @brief The event flag values.
* @{
*/
#define GADC_AUDIO_IN_LOSTEVENT 0x0001 /**< @brief The last GEVENT_AUDIO_IN event was lost */
#define GAUDIN_LOSTEVENT 0x0001 /**< @brief The last GEVENT_AUDIO_IN event was lost */
/** @} */
/**
* @brief The number of audio samples in the buffer
@ -123,7 +123,7 @@ bool_t gaudinInit(uint16_t channel, uint32_t frequency, audin_sample_t *buffer,
* @note The audio input 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 input.
* Once turned on it can only be turned off by calling @p gadcHighSpeedInit() again.
* Once turned on it can only be turned off by calling @p gaudinInit() again.
* @note The audio input is capable of signalling via this method and a binary semaphore
* at the same time.
*

123
src/gaudout/driver.h Normal file
View File

@ -0,0 +1,123 @@
/*
* 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/gaudout/driver.h
* @brief GAUDOUT - Audio Output driver header file.
*
* @defgroup Driver Driver
* @ingroup GAUDOUT
* @{
*/
#ifndef _GAUDOUT_LLD_H
#define _GAUDOUT_LLD_H
#include "gfx.h"
#if GFX_USE_GAUDOUT || 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 GAUDOUT code for use by the GAUDOUT drivers.
*
* @iclass
* @notapi
*/
GAudioData *gaudoutGetDataBlockI(void);
/**
* @brief Release a block of audio data after playing
*
* @param[in] paud The GAudioData block to be released.
*
* @note Defined in the high level GAUDOUT code for use by the GAUDOUT drivers.
*
* @iclass
* @notapi
*/
void gaudoutReleaseDataBlockI(GAudioData *paud);
/**
* @brief Initialise the driver
* @return TRUE if the channel and frequency 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
*
* @note The driver will always have been stopped and de-init before this is called.
*
* @api
*/
bool_t gaudout_lld_init(uint16_t channel, uint32_t frequency);
/**
* @brief De-Initialise the driver
*
* @note The audio output will always have been stopped first by the high level layer.
* @note This may be called before a @p gaudout_lld_init() has occurred.
*
* @api
*/
void gaudout_lld_deinit(void);
/**
* @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 gaudoutGetDataBlockI().
*
* @api
*/
void gaudout_lld_start(void);
/**
* @brief Stop the audio output playing.
*
* @note Some drivers may only stop playing at a data block boundary.
* @note This may be called before a @p gaudout_lld_init() has occurred.
*
* @api
*/
void gaudout_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 gaudout_lld_set_volume(uint8_t vol);
#ifdef __cplusplus
}
#endif
#endif /* GFX_USE_GAUDOUT */
#endif /* _GAUDOUT_LLD_H */
/** @} */

View File

@ -16,11 +16,47 @@
#if GFX_USE_GAUDOUT || defined(__DOXYGEN__)
#error "GAUDOUT: Not implemented yet"
#include "src/gaudout/driver.h"
static gfxQueueASync playlist;
static gfxQueueGSync freelist;
static uint16_t audFlags;
#define AUDOUTFLG_RUNNING 0x0001
#define AUDOUTFLG_USE_EVENTS 0x0002
#if GFX_USE_GEVENT
static GTimer AudGTimer;
static void AudGTimerCallback(void *param) {
(void) param;
GSourceListener *psl;
GEventADC *pe;
psl = 0;
while ((psl = geventGetSourceListener((GSourceHandle)(&aud), psl))) {
if (!(pe = (GEventAudioIn *)geventGetEventBuffer(psl))) {
// This listener is missing - save this.
psl->srcflags |= GAUDIN_LOSTEVENT;
continue;
}
pe->type = GEVENT_AUDIO_IN;
pe->channel = aud.channel;
pe->count = lastcount;
pe->buffer = lastbuffer;
pe->flags = psl->srcflags;
psl->srcflags = 0;
geventSendEvent(psl);
}
}
#endif
void _gaudoutInit(void)
{
/* ToDo */
gfxQueueASyncInit(&playlist);
gfxQueueGSyncInit(&freelist);
}
void _gaudoutDeinit(void)
@ -28,6 +64,77 @@ void _gaudoutDeinit(void)
/* ToDo */
}
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);
}
bool_t gaudioPlayInit(uint16_t channel, uint32_t frequency) {
gaudioPlayStop();
gaudout_lld_deinit();
return gaudout_lld_init(channel, frequency);
}
void gaudioPlay(GAudioData *paud) {
if (paud)
gfxQueueASyncPut(&playlist, (gfxQueueASyncItem *)paud);
gaudout_lld_start();
}
void gaudioPlayPause(void) {
gaudout_lld_stop();
}
void gaudioPlayStop(void) {
GAudioData *paud;
gaudout_lld_stop();
while((paud = (GAudioData *)gfxQueueASyncGet(&playlist)))
gfxQueueGSyncPut(&freelist, (gfxQueueGSyncItem *)paud);
}
bool_t gaudioPlaySetVolume(uint8_t vol) {
return gaudout_lld_set_volume(vol);
}
/**
* Routines provided for use by drivers.
*/
GAudioData *gaudoutGetDataBlockI(void) {
return (GAudioData *)gfxQueueASyncGet(&playlist);
}
void gaudoutReleaseDataBlockI(GAudioData *paud) {
gfxQueueGSyncPut(&freelist, (gfxQueueGSyncItem *)paud);
}
#endif /* GFX_USE_GAUDOUT */
/** @} */

View File

@ -22,10 +22,26 @@
#if GFX_USE_GAUDOUT || defined(__DOXYGEN__)
/* Include the driver defines */
#include "gaudout_lld_config.h"
/*===========================================================================*/
/* 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 samples)
} GAudioData;
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
@ -34,6 +50,111 @@
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);
/**
* @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.
* @param[in] frequency The audio sample rate in samples per second
*
* @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.
*
* @api
*/
bool_t gaudioPlayInit(uint16_t channel, uint32_t frequency);
/**
* @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. While the buffer size is specified in bytes, this length is specified in samples
* and must be even for stereo channels.
* @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);
#ifdef __cplusplus
}
#endif

View File

@ -17,6 +17,27 @@
#define _GAUDOUT_RULES_H
#if GFX_USE_GAUDOUT
#if !GFX_USE_GQUEUE
#if GFX_DISPLAY_RULE_WARNINGS
#warning "GAUDOUT: GFX_USE_GQUEUE is required if GFX_USE_GAUDOUT 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 "GAUDOUT: GQUEUE_NEED_ASYNC is required if GFX_USE_GAUDOUT 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 "GAUDOUT: GQUEUE_NEED_GSYNC is required if GFX_USE_GAUDOUT is TRUE. It has been turned on for you."
#endif
#undef GQUEUE_NEED_GSYNC
#define GQUEUE_NEED_GSYNC TRUE
#endif
#endif
#endif /* _GAUDOUT_RULES_H */