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.remotes/origin_old/ugfx_release_2.6
parent
944c33cbff
commit
ea5a1b849d
|
@ -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/audio/gadc/driver.mk
|
||||
include $(GFXLIB)/drivers/gaudio/gadc/driver.mk
|
||||
|
|
|
@ -6,7 +6,7 @@ On this board uGFX currently supports:
|
|||
- GADC via the AT91SAM7 driver
|
||||
- GINPUT-dials via the GADC driver
|
||||
- GINPUT-toggles via the Pal driver
|
||||
- GAUDIN via the GADC driver
|
||||
- GAUDIO recording via the GADC driver
|
||||
|
||||
Note there are two variants of this board - one with the GE8 display
|
||||
and one with the GE12 display. This one is for the GE12 display.
|
||||
|
|
|
@ -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/audio/gadc/driver.mk
|
||||
include $(GFXLIB)/drivers/gaudio/gadc/driver.mk
|
||||
|
|
|
@ -6,28 +6,28 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* @file boards/base/Olimex-SAM7EX256-GE8/gaudin_lld_board.h
|
||||
* @brief GAUDIN Driver board config file for the Olimex SAM7EX256 board
|
||||
* @file boards/base/Olimex-SAM7EX256-GE8/gaudio_record_board.h
|
||||
* @brief GAUDIO Record Driver board config file for the Olimex SAM7EX256 board
|
||||
*/
|
||||
|
||||
#ifndef _GAUDIN_LLD_BOARD_H
|
||||
#define _GAUDIN_LLD_BOARD_H
|
||||
#ifndef _GAUDIO_RECORD_BOARD_H
|
||||
#define _GAUDIO_RECORD_BOARD_H
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Audio inputs on this board */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define GAUDIN_NUM_CHANNELS 1
|
||||
#define GAUDIO_RECORD_NUM_CHANNELS 1
|
||||
|
||||
/**
|
||||
* The list of audio channels and their uses
|
||||
*/
|
||||
#define GAUDIN_MICROPHONE 0
|
||||
#define GAUDIO_RECORD_MICROPHONE 0
|
||||
|
||||
#ifdef GAUDIN_LLD_IMPLEMENTATION
|
||||
static uint32_t gaudin_lld_physdevs[GAUDIN_NUM_CHANNELS] = {
|
||||
#ifdef GAUDIO_RECORD_IMPLEMENTATION
|
||||
static uint32_t gaudin_lld_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = {
|
||||
GADC_PHYSDEV_MICROPHONE,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* _GAUDIN_LLD_BOARD_H */
|
||||
#endif /* _GAUDIO_RECORD_BOARD_H */
|
|
@ -6,7 +6,7 @@ On this board uGFX currently supports:
|
|||
- GADC via the AT91SAM7 driver
|
||||
- GINPUT-dials via the GADC driver
|
||||
- GINPUT-toggles via the Pal driver
|
||||
- GAUDIN via the GADC driver
|
||||
- GAUDIO recording via the GADC driver
|
||||
|
||||
Note there are two variants of this board - one with the GE8 display
|
||||
and one with the GE12 display. This one is for the GE8 display.
|
||||
|
|
|
@ -2,4 +2,4 @@ GFXINC += $(GFXLIB)/boards/base/Win32
|
|||
GFXSRC +=
|
||||
|
||||
include $(GFXLIB)/drivers/multiple/Win32/driver.mk
|
||||
include $(GFXLIB)/drivers/audio/Win32/driver.mk
|
||||
include $(GFXLIB)/drivers/gaudio/Win32/driver.mk
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
DEMODIR = $(GFXLIB)/demos/modules/audio/oscilloscope
|
||||
DEMODIR = $(GFXLIB)/demos/modules/gaudio/oscilloscope
|
||||
GFXINC += $(DEMODIR)
|
||||
GFXSRC += $(DEMODIR)/main.c $(DEMODIR)/gwinosc.c
|
|
@ -46,11 +46,14 @@
|
|||
#define GFX_USE_GWIN TRUE
|
||||
#define GFX_USE_GTIMER TRUE
|
||||
//#define GFX_USE_GADC TRUE
|
||||
#define GFX_USE_GAUDIN TRUE
|
||||
#define GFX_USE_GAUDIO TRUE
|
||||
|
||||
/* Features for the GDISP sub-system. */
|
||||
#define GDISP_NEED_VALIDATION TRUE
|
||||
#define GDISP_NEED_CLIP TRUE
|
||||
#define GDISP_NEED_MULTITHREAD TRUE
|
||||
|
||||
/* Features for the GAUDIO sub-system */
|
||||
#define GAUDIO_NEED_RECORD TRUE
|
||||
|
||||
#endif /* _GFXCONF_H */
|
|
@ -32,8 +32,8 @@
|
|||
*
|
||||
* This GWIN superset implements a simple audio oscilloscope using the GAUDIN module.
|
||||
*
|
||||
* It makes many assumptions, the most fundamental of which is that the audio device
|
||||
* produces unsigned integer samples.
|
||||
* It makes many assumptions, the most fundamental of which is that the audio data
|
||||
* should be scaled to SCOPE_Y_BITS.
|
||||
*
|
||||
* The GMISC module with GMISC_NEED_ARRAYOPS could be used to process the samples more
|
||||
* correctly if we were really building something generic.
|
||||
|
@ -45,22 +45,15 @@
|
|||
/* Include internal GWIN routines so we can build our own superset class */
|
||||
#include "src/gwin/class_gwin.h"
|
||||
|
||||
/* The size of our dynamically allocated audio buffer */
|
||||
#define AUDIOBUFSZ 64*2
|
||||
|
||||
/* How many flat-line sample before we trigger */
|
||||
#define FLATLINE_SAMPLES 8
|
||||
|
||||
static void _destroy(GHandle gh) {
|
||||
gaudinStop();
|
||||
gaudioRecordStop();
|
||||
if (((GScopeObject *)gh)->lastscopetrace) {
|
||||
gfxFree(((GScopeObject *)gh)->lastscopetrace);
|
||||
((GScopeObject *)gh)->lastscopetrace = 0;
|
||||
}
|
||||
if (((GScopeObject *)gh)->audiobuf) {
|
||||
gfxFree(((GScopeObject *)gh)->audiobuf);
|
||||
((GScopeObject *)gh)->audiobuf = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const gwinVMT scopeVMT = {
|
||||
|
@ -71,127 +64,136 @@ static const gwinVMT scopeVMT = {
|
|||
0, // The after-clear routine
|
||||
};
|
||||
|
||||
GHandle gwinGScopeCreate(GDisplay *g, GScopeObject *gs, GWindowInit *pInit, uint16_t channel, uint32_t frequency) {
|
||||
GHandle gwinGScopeCreate(GDisplay *g, GScopeObject *gs, GWindowInit *pInit, uint16_t channel, uint32_t frequency, ArrayDataFormat format) {
|
||||
/* Make sure the audio parameters are valid first */
|
||||
if (!gaudioRecordInit(channel, frequency, format))
|
||||
return 0;
|
||||
|
||||
/* Initialise the base class GWIN */
|
||||
if (!(gs = (GScopeObject *)_gwindowCreate(g, &gs->g, pInit, &scopeVMT, 0)))
|
||||
return 0;
|
||||
|
||||
/* Initialise the scope object members and allocate memory for buffers */
|
||||
gfxSemInit(&gs->bsem, 0, 1);
|
||||
gs->format = format;
|
||||
gs->nextx = 0;
|
||||
if (!(gs->lastscopetrace = (coord_t *)gfxAlloc(gs->g.width * sizeof(coord_t))))
|
||||
return 0;
|
||||
if (!(gs->audiobuf = (audin_sample_t *)gfxAlloc(AUDIOBUFSZ * sizeof(audin_sample_t))))
|
||||
return 0;
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
gs->lasty = gs->g.height/2;
|
||||
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
gs->lasty = gs->g.height/2;
|
||||
gs->scopemin = 0;
|
||||
#endif
|
||||
|
||||
/* Start the GADC high speed converter */
|
||||
gaudinInit(channel, frequency, gs->audiobuf, AUDIOBUFSZ, AUDIOBUFSZ/2);
|
||||
gaudinSetBSem(&gs->bsem, &gs->myEvent);
|
||||
gaudinStart();
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
gs->lasty = gs->g.height/2;
|
||||
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
gs->lasty = gs->g.height/2;
|
||||
gs->scopemin = 0;
|
||||
#endif
|
||||
|
||||
/* Set visibility */
|
||||
gwinSetVisible((GHandle)gs, pInit->show);
|
||||
|
||||
/* Start the audio recording */
|
||||
gaudioRecordStart();
|
||||
|
||||
return (GHandle)gs;
|
||||
}
|
||||
|
||||
void gwinScopeWaitForTrace(GHandle gh) {
|
||||
#define gs ((GScopeObject *)(gh))
|
||||
GAudioData *paud;
|
||||
int i;
|
||||
coord_t x, y;
|
||||
coord_t yoffset;
|
||||
audin_sample_t *pa;
|
||||
uint8_t *pa8;
|
||||
uint16_t *pa16;
|
||||
coord_t *pc;
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
bool_t rdytrigger;
|
||||
int flsamples;
|
||||
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
bool_t rdytrigger;
|
||||
int flsamples;
|
||||
coord_t scopemin;
|
||||
#endif
|
||||
uint8_t shr;
|
||||
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
bool_t rdytrigger;
|
||||
int flsamples;
|
||||
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
bool_t rdytrigger;
|
||||
int flsamples;
|
||||
coord_t scopemin;
|
||||
#endif
|
||||
|
||||
if (gh->vmt != &scopeVMT)
|
||||
return;
|
||||
|
||||
/* Wait for a set of audio conversions */
|
||||
gfxSemWait(&gs->bsem, TIME_INFINITE);
|
||||
paud = gaudioRecordGetData(TIME_INFINITE);
|
||||
|
||||
/* Ensure we are drawing in the right area */
|
||||
#if GDISP_NEED_CLIP
|
||||
gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
|
||||
#endif
|
||||
|
||||
shr = 16 - gfxSampleFormatBits(gs->format);
|
||||
yoffset = gh->height/2;
|
||||
if (!(GAUDIN_SAMPLE_FORMAT & 1))
|
||||
if (!gfxSampleFormatIsSigned(gs->format))
|
||||
yoffset += (1<<SCOPE_Y_BITS)/2;
|
||||
|
||||
x = gs->nextx;
|
||||
pc = gs->lastscopetrace+x;
|
||||
pa = gs->myEvent.buffer;
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
rdytrigger = FALSE;
|
||||
flsamples = 0;
|
||||
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
rdytrigger = FALSE;
|
||||
flsamples = 0;
|
||||
scopemin = 0;
|
||||
#endif
|
||||
pa8 = (uint8_t *)(paud+1);
|
||||
pa16 = (uint16_t *)(paud+1);
|
||||
|
||||
for(i = gs->myEvent.count; i; i--) {
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
rdytrigger = FALSE;
|
||||
flsamples = 0;
|
||||
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
rdytrigger = FALSE;
|
||||
flsamples = 0;
|
||||
scopemin = 0;
|
||||
#endif
|
||||
|
||||
for(i = paud->len/(gfxSampleFormatBits(gs->format)/8); i; i--) {
|
||||
|
||||
/* Calculate the new scope value - re-scale using simple shifts for efficiency, re-center and y-invert */
|
||||
#if GAUDIN_BITS_PER_SAMPLE > SCOPE_Y_BITS
|
||||
y = yoffset - (*pa++ >> (GAUDIN_BITS_PER_SAMPLE - SCOPE_Y_BITS));
|
||||
#else
|
||||
y = yoffset - (*pa++ << (SCOPE_Y_BITS - GAUDIN_BITS_PER_SAMPLE));
|
||||
#endif
|
||||
if (gs->format <= 8)
|
||||
y = yoffset - (((coord_t)(*pa8++ ) << shr) >> (16-SCOPE_Y_BITS));
|
||||
else
|
||||
y = yoffset - (((coord_t)(*pa16++) << shr) >> (16-SCOPE_Y_BITS));
|
||||
|
||||
#if TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
/* Calculate the scopemin ready for the next trace */
|
||||
if (y > scopemin)
|
||||
scopemin = y;
|
||||
#endif
|
||||
#if TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
/* Calculate the scopemin ready for the next trace */
|
||||
if (y > scopemin)
|
||||
scopemin = y;
|
||||
#endif
|
||||
|
||||
/* Have we reached the end of a scope trace? */
|
||||
if (x >= gh->width) {
|
||||
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP || TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
/* Handle triggering - we trigger on the next sample minimum (y value maximum) or a flat-line */
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP || TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
/* Handle triggering - we trigger on the next sample minimum (y value maximum) or a flat-line */
|
||||
|
||||
#if TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
/* Arm when we reach the sample minimum (y value maximum) of the previous trace */
|
||||
if (!rdytrigger && y >= gs->scopemin)
|
||||
rdytrigger = TRUE;
|
||||
#endif
|
||||
|
||||
if (y == gs->lasty) {
|
||||
/* Trigger if we get too many flat-line samples regardless of the armed state */
|
||||
if (++flsamples < FLATLINE_SAMPLES)
|
||||
continue;
|
||||
flsamples = 0;
|
||||
} else if (y > gs->lasty) {
|
||||
gs->lasty = y;
|
||||
flsamples = 0;
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
/* Arm the trigger when samples fall (y increases) ie. negative slope */
|
||||
rdytrigger = TRUE;
|
||||
#if TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
/* Arm when we reach the sample minimum (y value maximum) of the previous trace */
|
||||
if (!rdytrigger && y >= gs->scopemin)
|
||||
rdytrigger = TRUE;
|
||||
#endif
|
||||
continue;
|
||||
} else {
|
||||
/* If the trigger is armed, Trigger when samples increases (y decreases) ie. positive slope */
|
||||
gs->lasty = y;
|
||||
flsamples = 0;
|
||||
if (!rdytrigger)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ready for a the next trigger cycle */
|
||||
rdytrigger = FALSE;
|
||||
#endif
|
||||
if (y == gs->lasty) {
|
||||
/* Trigger if we get too many flat-line samples regardless of the armed state */
|
||||
if (++flsamples < FLATLINE_SAMPLES)
|
||||
continue;
|
||||
flsamples = 0;
|
||||
} else if (y > gs->lasty) {
|
||||
gs->lasty = y;
|
||||
flsamples = 0;
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
/* Arm the trigger when samples fall (y increases) ie. negative slope */
|
||||
rdytrigger = TRUE;
|
||||
#endif
|
||||
continue;
|
||||
} else {
|
||||
/* If the trigger is armed, Trigger when samples increases (y decreases) ie. positive slope */
|
||||
gs->lasty = y;
|
||||
flsamples = 0;
|
||||
if (!rdytrigger)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ready for a the next trigger cycle */
|
||||
rdytrigger = FALSE;
|
||||
#endif
|
||||
|
||||
/* Prepare for a scope trace */
|
||||
x = 0;
|
||||
|
@ -214,5 +216,6 @@ void gwinScopeWaitForTrace(GHandle gh) {
|
|||
gs->scopemin = scopemin;
|
||||
#endif
|
||||
|
||||
gaudioReleaseBuffer(paud);
|
||||
#undef gs
|
||||
}
|
|
@ -64,9 +64,7 @@ typedef struct GScopeObject_t {
|
|||
GWindowObject g; // Base Class
|
||||
|
||||
coord_t *lastscopetrace; // To store last scope trace
|
||||
gfxSem bsem; // We get signalled on this
|
||||
audin_sample_t *audiobuf; // To store audio samples
|
||||
GEventAudioIn myEvent; // Information on received samples
|
||||
ArrayDataFormat format; // The sample format
|
||||
coord_t nextx; // Where we are up to
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
coord_t lasty; // The last y value - used for trigger slope detection
|
||||
|
@ -83,8 +81,8 @@ extern "C" {
|
|||
/**
|
||||
* Create a scope window.
|
||||
*/
|
||||
GHandle gwinGScopeCreate(GDisplay *g, GScopeObject *gs, GWindowInit *pInit, uint16_t channel, uint32_t frequency);
|
||||
#define gwinScopeCreate(gs,pI,ch,f) gwinGScopeCreate(GDISP,gs,pI,ch,f)
|
||||
GHandle gwinGScopeCreate(GDisplay *g, GScopeObject *gs, GWindowInit *pInit, uint16_t channel, uint32_t frequency, ArrayDataFormat format);
|
||||
#define gwinScopeCreate(gs,pI,ch,f,fmt) gwinGScopeCreate(GDISP,gs,pI,ch,f,fmt)
|
||||
|
||||
/**
|
||||
* Wait for a scope trace to be ready and then draw it.
|
|
@ -39,8 +39,9 @@
|
|||
#include "gwinosc.h"
|
||||
|
||||
/* Specify our timing parameters */
|
||||
#define MY_AUDIO_FREQUENCY 4000 /* 4khz */
|
||||
#define MY_AUDIO_CHANNEL 0 /* Use channel 0 */
|
||||
#define MY_AUDIO_FREQUENCY 8000 /* 8khz. If this is too much try 4000 (4khz) */
|
||||
#define MY_AUDIO_CHANNEL 0 /* Use channel 0 - must be a mono channel */
|
||||
#define MY_AUDIO_FORMAT GAUDIO_RECORD_FORMAT1 /* The default format */
|
||||
|
||||
/* Data */
|
||||
static GScopeObject gScopeWindow;
|
||||
|
@ -54,6 +55,11 @@ int main(void) {
|
|||
|
||||
gfxInit();
|
||||
|
||||
// Allocate audio buffers - 4 x 128 byte buffers.
|
||||
// You may need to increase this for slower cpu's.
|
||||
// You may be able to decrease this for low latency operating systems.
|
||||
gaudioAllocBuffers(4, 128);
|
||||
|
||||
/* Get the screen dimensions */
|
||||
swidth = gdispGetWidth();
|
||||
sheight = gdispGetHeight();
|
||||
|
@ -65,7 +71,7 @@ int main(void) {
|
|||
wi.show = TRUE;
|
||||
wi.x = wi.y = 0;
|
||||
wi.width = swidth; wi.height = sheight;
|
||||
ghScope = gwinScopeCreate(&gScopeWindow, &wi, MY_AUDIO_CHANNEL, MY_AUDIO_FREQUENCY);
|
||||
ghScope = gwinScopeCreate(&gScopeWindow, &wi, MY_AUDIO_CHANNEL, MY_AUDIO_FREQUENCY, MY_AUDIO_FORMAT);
|
||||
}
|
||||
gwinSetBgColor(ghScope, White);
|
||||
gwinSetColor(ghScope, Red);
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
@ -1,3 +1,3 @@
|
|||
DEMODIR = $(GFXLIB)/demos/modules/audio/play-wave
|
||||
DEMODIR = $(GFXLIB)/demos/modules/gaudio/play-wave
|
||||
GFXINC += $(DEMODIR)
|
||||
GFXSRC += $(DEMODIR)/main.c
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
/* GFX sub-systems to turn on */
|
||||
#define GFX_USE_GDISP TRUE
|
||||
#define GFX_USE_GAUDOUT TRUE
|
||||
#define GFX_USE_GAUDIO TRUE
|
||||
#define GFX_USE_GFILE TRUE
|
||||
|
||||
/* Features for the GDISP sub-system. */
|
||||
|
@ -53,6 +53,9 @@
|
|||
/* GDISP fonts to include */
|
||||
#define GDISP_INCLUDE_FONT_UI2 TRUE
|
||||
|
||||
/* Features for the GAUDIO sub-system */
|
||||
#define GAUDIO_NEED_PLAY TRUE
|
||||
|
||||
/* Features for the GFILE sub-system */
|
||||
#define GFILE_NEED_ROMFS TRUE
|
||||
|
|
@ -34,8 +34,8 @@
|
|||
#include "gfx.h"
|
||||
|
||||
/* Specify our timing parameters */
|
||||
#define MY_AUDIO_CHANNEL 0 /* Use channel 0 */
|
||||
#define MY_AUDIO_CHANNEL_IS_STEREO GAUDOUT_CHANNEL0_STEREO /* Is it stereo? */
|
||||
#define MY_AUDIO_CHANNEL 0 /* Use channel 0 */
|
||||
#define MY_AUDIO_CHANNEL_IS_STEREO GAUDOUT_CHANNEL0_IS_STEREO /* Is it stereo? */
|
||||
|
||||
// Storage for the wave header
|
||||
static char whdr[32];
|
||||
|
@ -61,7 +61,9 @@ int main(void) {
|
|||
// Any font will do
|
||||
font = gdispOpenFont("*");
|
||||
|
||||
// Allocate audio buffers - 4 x 512 byte buffers. You may need to increase this for slower cpu's.
|
||||
// Allocate audio buffers - 4 x 512 byte buffers.
|
||||
// You may need to increase this for slower cpu's.
|
||||
// You may be able to decrease this for low latency operating systems.
|
||||
if (!gaudioAllocBuffers(4, 512)) {
|
||||
errmsg = "Err: No Memory";
|
||||
goto theend;
|
||||
|
@ -179,8 +181,8 @@ int main(void) {
|
|||
}
|
||||
gfileClose(f);
|
||||
|
||||
// Wait 3 seconds for the play to finish - FIX THIS
|
||||
gfxSleepMilliseconds(3000);
|
||||
// Wait for the play to finish
|
||||
gaudioPlayWait(TIME_INFINITE);
|
||||
gdispDrawString(0, gdispGetHeight()/2+10, "Done", font, Green);
|
||||
|
||||
// The end
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* This file was generated from "allwrong.wav" using...
|
||||
*
|
||||
* file2c -bcs allwrong.wav romfs_allwrong.h
|
||||
* file2c -dcs allwrong.wav romfs_allwrong.h
|
||||
*
|
||||
*/
|
||||
static const char allwrong[] = {
|
|
@ -1,6 +0,0 @@
|
|||
# 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
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
* 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/gaudin_lld.c
|
||||
* @brief GAUDIN - Driver file for Win32.
|
||||
*/
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
#if GFX_USE_GAUDIN
|
||||
|
||||
/* Include the driver defines */
|
||||
#include "src/gaudin/driver.h"
|
||||
|
||||
#undef Red
|
||||
#undef Green
|
||||
#undef Blue
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
static HWAVEIN ah;
|
||||
static volatile int nQueuedBuffers;
|
||||
static bool_t isClosing;
|
||||
static WAVEHDR *pWaveHdrs;
|
||||
static HANDLE waveThread;
|
||||
static DWORD threadID;
|
||||
|
||||
/*
|
||||
static void PrintWaveErrorMsg(DWORD err, TCHAR * str)
|
||||
{
|
||||
#define BUFFERSIZE 128
|
||||
char buffer[BUFFERSIZE];
|
||||
|
||||
fprintf(stderr, "GAUDIN: 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 waveInAddBuffer() which is what we need to use whenever we receive a
|
||||
* MM_WIM_DATA. My callback would need to defer that job to another thread
|
||||
* anyway, so instead just use CALLBACK_THREAD here instead.
|
||||
*************************************************************************/
|
||||
|
||||
static DWORD WINAPI waveProc(LPVOID arg) {
|
||||
MSG msg;
|
||||
(void) arg;
|
||||
|
||||
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 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));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* 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) {
|
||||
WAVEFORMATEX wfx;
|
||||
size_t spaceleft;
|
||||
audin_sample_t *p;
|
||||
WAVEHDR *phdr;
|
||||
size_t nBuffers;
|
||||
size_t sz;
|
||||
|
||||
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(waveThread);
|
||||
}
|
||||
|
||||
gaudin_lld_deinit();
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = paud->channel == GAUDIN_STEREO ? 2 : 1;
|
||||
wfx.nSamplesPerSec = paud->frequency;
|
||||
wfx.nBlockAlign = wfx.nChannels * sizeof(audin_sample_t);
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
wfx.wBitsPerSample = sizeof(audin_sample_t) * 8;
|
||||
wfx.cbSize = 0;
|
||||
|
||||
if (waveInOpen(&ah, WAVE_MAPPER, &wfx, (DWORD_PTR)threadID, (DWORD_PTR)paud, CALLBACK_THREAD)) {
|
||||
fprintf(stderr, "GAUDIN: Can't open WAVE recording device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* We need to allocate a wave header for each buffer */
|
||||
nBuffers = (paud->bufcount + paud->samplesPerEvent - 1) / paud->samplesPerEvent;
|
||||
if (!(pWaveHdrs = gfxAlloc(nBuffers * sizeof(WAVEHDR)))) {
|
||||
fprintf(stderr, "GAUDIN: Buffer header allocation failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prepare each buffer and send to the wavein device */
|
||||
spaceleft = paud->bufcount;
|
||||
for(p = paud->buffer, phdr = pWaveHdrs, spaceleft = paud->bufcount; spaceleft; p += sz, phdr++, spaceleft -= sz) {
|
||||
sz = spaceleft > paud->samplesPerEvent ? paud->samplesPerEvent : spaceleft;
|
||||
phdr->dwBufferLength = sz * sizeof(audin_sample_t);
|
||||
phdr->lpData = (LPSTR)p;
|
||||
phdr->dwFlags = 0;
|
||||
if (!waveInPrepareHeader(ah, phdr, sizeof(WAVEHDR))
|
||||
&& !waveInAddBuffer(ah, phdr, sizeof(WAVEHDR)))
|
||||
nQueuedBuffers++;
|
||||
else
|
||||
fprintf(stderr, "GAUDIN: Buffer prepare failed\n");
|
||||
}
|
||||
if (!nQueuedBuffers)
|
||||
fprintf(stderr, "GAUDIN: Failed to prepare any buffers\n");
|
||||
}
|
||||
|
||||
void gaudin_lld_start(void) {
|
||||
waveInStart(ah);
|
||||
}
|
||||
|
||||
void gaudin_lld_stop(void) {
|
||||
waveInStop(ah);
|
||||
}
|
||||
|
||||
#endif /* GFX_USE_GAUDIN */
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* 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/gaudin_lld_config.h
|
||||
* @brief GAUDIN Driver config file.
|
||||
*
|
||||
* @addtogroup GAUDIN
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef GAUDIN_LLD_CONFIG_H
|
||||
#define GAUDIN_LLD_CONFIG_H
|
||||
|
||||
#if GFX_USE_GAUDIN
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver hardware support. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief The audio input sample type
|
||||
*/
|
||||
//typedef uint8_t audin_sample_t;
|
||||
typedef int16_t audin_sample_t;
|
||||
|
||||
/**
|
||||
* @brief The maximum sample frequency supported by this audio device
|
||||
*/
|
||||
#define GAUDIN_MAX_SAMPLE_FREQUENCY 44100
|
||||
|
||||
/**
|
||||
* @brief The number of bits in a sample
|
||||
*/
|
||||
//#define GAUDIN_BITS_PER_SAMPLE 8
|
||||
#define GAUDIN_BITS_PER_SAMPLE 16
|
||||
|
||||
/**
|
||||
* @brief The format of an audio sample
|
||||
*/
|
||||
//#define GAUDIN_SAMPLE_FORMAT ARRAY_DATA_8BITUNSIGNED
|
||||
#define GAUDIN_SAMPLE_FORMAT ARRAY_DATA_16BITSIGNED
|
||||
|
||||
/**
|
||||
* @brief The number of audio channels supported by this driver
|
||||
*/
|
||||
#define GAUDIN_NUM_CHANNELS 2
|
||||
|
||||
/**
|
||||
* @brief The list of audio channels and their uses
|
||||
* @{
|
||||
*/
|
||||
#define GAUDIN_MONO 0
|
||||
#define GAUDIN_STEREO 1
|
||||
/** @} */
|
||||
|
||||
#endif /* GFX_USE_GAUDIN */
|
||||
|
||||
#endif /* GAUDIN_LLD_CONFIG_H */
|
||||
/** @} */
|
|
@ -1,10 +0,0 @@
|
|||
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.
|
|
@ -1,5 +0,0 @@
|
|||
# List the required driver.
|
||||
GFXSRC += $(GFXLIB)/drivers/gaudin/gadc/gaudin_lld.c
|
||||
|
||||
# Required include directories
|
||||
GFXINC += $(GFXLIB)/drivers/audio/gadc
|
|
@ -0,0 +1,6 @@
|
|||
# List the required driver.
|
||||
GFXSRC += $(GFXLIB)/drivers/gaudio/Win32/gaudio_record_lld.c \
|
||||
$(GFXLIB)/drivers/gaudio/Win32/gaudio_play_lld.c
|
||||
|
||||
# Required include directories
|
||||
GFXINC += $(GFXLIB)/drivers/gaudio/Win32
|
|
@ -6,17 +6,17 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* @file drivers/audio/Win32/gaudout_lld_config.h
|
||||
* @brief GAUDOUT Driver config file.
|
||||
* @file drivers/gaudio/Win32/gaudio_play_config.h
|
||||
* @brief GAUDIO Play Driver config file.
|
||||
*
|
||||
* @addtogroup GAUDOUT
|
||||
* @addtogroup GAUDIO
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef GAUDOUT_LLD_CONFIG_H
|
||||
#define GAUDIN_LLD_CONFIG_H
|
||||
#ifndef GAUDIO_PLAY_CONFIG_H
|
||||
#define GAUDIO_PLAY_CONFIG_H
|
||||
|
||||
#if GFX_USE_GAUDOUT
|
||||
#if GFX_USE_GAUDIO && GAUDIO_NEED_PLAY
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver hardware support. */
|
||||
|
@ -25,39 +25,39 @@
|
|||
/**
|
||||
* @brief The maximum sample frequency supported by this audio device
|
||||
*/
|
||||
#define GAUDOUT_MAX_SAMPLE_FREQUENCY 44100
|
||||
#define GAUDIO_PLAY_MAX_SAMPLE_FREQUENCY 44100
|
||||
|
||||
/**
|
||||
* @brief The number of audio formats supported by this driver
|
||||
*/
|
||||
#define GAUDOUT_NUM_FORMATS 2
|
||||
#define GAUDIO_PLAY_NUM_FORMATS 2
|
||||
|
||||
/**
|
||||
* @brief The available audio sample formats in order of preference
|
||||
*/
|
||||
#define GAUDOUT_FORMAT1 ARRAY_DATA_16BITSIGNED
|
||||
#define GAUDOUT_FORMAT2 ARRAY_DATA_8BITUNSIGNED
|
||||
#define GAUDIO_PLAY_FORMAT1 ARRAY_DATA_16BITSIGNED
|
||||
#define GAUDIO_PLAY_FORMAT2 ARRAY_DATA_8BITUNSIGNED
|
||||
|
||||
/**
|
||||
* @brief The number of audio channels supported by this driver
|
||||
*/
|
||||
#define GAUDOUT_NUM_CHANNELS 2
|
||||
#define GAUDIO_PLAY_NUM_CHANNELS 2
|
||||
|
||||
/**
|
||||
* @brief Whether each channel is mono or stereo
|
||||
*/
|
||||
#define GAUDOUT_CHANNEL0_STEREO FALSE
|
||||
#define GAUDOUT_CHANNEL1_STEREO TRUE
|
||||
#define GAUDIO_PLAY_CHANNEL0_IS_STEREO FALSE
|
||||
#define GAUDIO_PLAY_CHANNEL1_IS_STEREO TRUE
|
||||
|
||||
/**
|
||||
* @brief The list of audio channel names and their uses
|
||||
* @{
|
||||
*/
|
||||
#define GAUDOUT_MONO 0
|
||||
#define GAUDOUT_STEREO 1
|
||||
#define GAUDIO_PLAY_MONO 0
|
||||
#define GAUDIO_PLAY_STEREO 1
|
||||
/** @} */
|
||||
|
||||
#endif /* GFX_USE_GAUDOUT */
|
||||
#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_PLAY */
|
||||
|
||||
#endif /* GAUDOUT_LLD_CONFIG_H */
|
||||
#endif /* GAUDIO_PLAY_CONFIG_H */
|
||||
/** @} */
|
|
@ -6,16 +6,16 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* @file drivers/audio/Win32/gaudout_lld.c
|
||||
* @brief GAUDOUT - Driver file for Win32.
|
||||
* @file drivers/gaudio/Win32/gaudio_play_lld.c
|
||||
* @brief GAUDIO - Play Driver file for Win32.
|
||||
*/
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
#if GFX_USE_GAUDOUT
|
||||
#if GFX_USE_GAUDIO && GAUDIO_NEED_PLAY
|
||||
|
||||
/* Include the driver defines */
|
||||
#include "src/gaudout/driver.h"
|
||||
#include "src/gaudio/driver_play.h"
|
||||
|
||||
#undef Red
|
||||
#undef Green
|
||||
|
@ -36,20 +36,6 @@ 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,
|
||||
|
@ -65,7 +51,9 @@ static bool_t senddata(WAVEHDR *pwh) {
|
|||
|
||||
// Get the next data block to send
|
||||
gfxSystemLock();
|
||||
paud = gaudoutGetDataBlockI();
|
||||
paud = gaudioPlayGetDataBlockI();
|
||||
if (!paud && !nQueuedBuffers)
|
||||
gaudioPlayDoneI();
|
||||
gfxSystemUnlock();
|
||||
if (!paud)
|
||||
return FALSE;
|
||||
|
@ -77,13 +65,13 @@ static bool_t senddata(WAVEHDR *pwh) {
|
|||
pwh->dwFlags = 0;
|
||||
pwh->dwLoops = 0;
|
||||
if (waveOutPrepareHeader(ah, pwh, sizeof(WAVEHDR))) {
|
||||
fprintf(stderr, "GAUDOUT: Failed to prepare a buffer");
|
||||
fprintf(stderr, "GAUDIO: Failed to prepare a play buffer");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Send it to windows
|
||||
if (waveOutWrite(ah, pwh, sizeof(WAVEHDR))) {
|
||||
fprintf(stderr, "GAUDOUT: Failed to write the buffer");
|
||||
fprintf(stderr, "GAUDIO: Failed to write the play buffer");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
@ -106,18 +94,25 @@ static DWORD WINAPI waveProc(LPVOID arg) {
|
|||
|
||||
// Give the buffer back to the Audio Free List
|
||||
gfxSystemLock();
|
||||
gaudoutReleaseDataBlockI((GAudioData *)pwh->dwUser);
|
||||
gaudioPlayReleaseDataBlockI((GAudioData *)pwh->dwUser);
|
||||
gfxSystemUnlock();
|
||||
pwh->lpData = 0;
|
||||
nQueuedBuffers--;
|
||||
|
||||
// Try and get a new block
|
||||
if ((!isRunning || !senddata(pwh)) && !nQueuedBuffers) {
|
||||
gfxSystemLock();
|
||||
gaudoutDoneI();
|
||||
gfxSystemUnlock();
|
||||
// Are we stopping?
|
||||
if (!isRunning) {
|
||||
// Have we finished yet?
|
||||
if (!nQueuedBuffers) {
|
||||
gfxSystemLock();
|
||||
gaudioPlayDoneI();
|
||||
gfxSystemUnlock();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// Try and get a new block
|
||||
senddata(pwh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -127,17 +122,7 @@ static DWORD WINAPI waveProc(LPVOID arg) {
|
|||
/* 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, ArrayDataFormat format) {
|
||||
bool_t gaudio_play_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format) {
|
||||
WAVEFORMATEX wfx;
|
||||
|
||||
if (format != ARRAY_DATA_8BITUNSIGNED && format != ARRAY_DATA_16BITSIGNED)
|
||||
|
@ -145,40 +130,41 @@ bool_t gaudout_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat fo
|
|||
|
||||
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");
|
||||
fprintf(stderr, "GAUDIO: Can't create WAVE play-back thread\n");
|
||||
exit(-1);
|
||||
}
|
||||
CloseHandle(waveThread);
|
||||
}
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = channel == GAUDOUT_STEREO ? 2 : 1;
|
||||
wfx.nChannels = channel == GAUDIO_PLAY_STEREO ? 2 : 1;
|
||||
wfx.nSamplesPerSec = frequency;
|
||||
wfx.nBlockAlign = wfx.nChannels * (format == ARRAY_DATA_8BITUNSIGNED ? 1 : 2);
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
wfx.wBitsPerSample = (format == ARRAY_DATA_8BITUNSIGNED ? 8 : 16);
|
||||
wfx.cbSize = 0;
|
||||
|
||||
if (ah) {
|
||||
waveOutClose(ah);
|
||||
ah = 0;
|
||||
}
|
||||
if (waveOutOpen(&ah, WAVE_MAPPER, &wfx, (DWORD_PTR)threadID, 0, CALLBACK_THREAD)) {
|
||||
fprintf(stderr, "GAUDOUT: Can't open WAVE play-back device\n");
|
||||
fprintf(stderr, "GAUDIO: Can't open WAVE play-back device\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool_t gaudout_lld_set_volume(uint8_t vol) {
|
||||
bool_t gaudio_play_lld_set_volume(uint8_t vol) {
|
||||
if (!ah)
|
||||
return FALSE;
|
||||
return waveOutSetVolume(ah, (((uint16_t)vol)<<8)|vol) != 0;
|
||||
}
|
||||
|
||||
void gaudout_lld_start(void) {
|
||||
void gaudio_play_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.
|
||||
|
@ -190,10 +176,10 @@ void gaudout_lld_start(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void gaudout_lld_stop(void) {
|
||||
void gaudio_play_lld_stop(void) {
|
||||
isRunning = FALSE;
|
||||
if (ah)
|
||||
waveOutReset(ah);
|
||||
waveOutReset(ah);
|
||||
while(nQueuedBuffers) Sleep(1);
|
||||
}
|
||||
|
||||
#endif /* GFX_USE_GAUDOUT */
|
||||
#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_PLAY */
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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/gaudio/Win32/gaudio_record_config.h
|
||||
* @brief GAUDIO Record Driver config file.
|
||||
*
|
||||
* @addtogroup GAUDIO
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef GAUDIO_RECORD_CONFIG_H
|
||||
#define GAUDIO_RECORD_CONFIG_H
|
||||
|
||||
#if GFX_USE_GAUDIO && GAUDIO_NEED_RECORD
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver hardware support. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief The maximum sample frequency supported by this audio device
|
||||
*/
|
||||
#define GAUDIO_RECORD_MAX_SAMPLE_FREQUENCY 44100
|
||||
|
||||
/**
|
||||
* @brief The number of audio formats supported by this driver
|
||||
*/
|
||||
#define GAUDIO_RECORD_NUM_FORMATS 2
|
||||
|
||||
/**
|
||||
* @brief The available audio sample formats in order of preference
|
||||
*/
|
||||
#define GAUDIO_RECORD_FORMAT1 ARRAY_DATA_16BITSIGNED
|
||||
#define GAUDIO_RECORD_FORMAT2 ARRAY_DATA_8BITUNSIGNED
|
||||
|
||||
/**
|
||||
* @brief The number of audio channels supported by this driver
|
||||
*/
|
||||
#define GAUDIO_RECORD_NUM_CHANNELS 2
|
||||
|
||||
/**
|
||||
* @brief Whether each channel is mono or stereo
|
||||
*/
|
||||
#define GAUDIO_RECORD_CHANNEL0_IS_STEREO FALSE
|
||||
#define GAUDIO_RECORD_CHANNEL1_IS_STEREO TRUE
|
||||
|
||||
/**
|
||||
* @brief The list of audio channels and their uses
|
||||
* @{
|
||||
*/
|
||||
#define GAUDIO_RECORD_MONO 0
|
||||
#define GAUDIO_RECORD_STEREO 1
|
||||
/** @} */
|
||||
|
||||
#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_RECORD */
|
||||
|
||||
#endif /* GAUDIO_RECORD_CONFIG_H */
|
||||
/** @} */
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* 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/gaudio/Win32/gaudio_record_lld.c
|
||||
* @brief GAUDIO - Record Driver file for Win32.
|
||||
*/
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
#if GFX_USE_GAUDIO && GAUDIO_NEED_RECORD
|
||||
|
||||
/* Include the driver defines */
|
||||
#include "src/gaudio/driver_record.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 chews buffers on the free-list.
|
||||
|
||||
static HWAVEIN ah = 0;
|
||||
static volatile int nQueuedBuffers;
|
||||
static bool_t isRunning;
|
||||
static WAVEHDR WaveHdrs[MAX_WAVE_HEADERS];
|
||||
static HANDLE waveThread;
|
||||
static DWORD threadID;
|
||||
|
||||
/**************************** 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 waveInAddBuffer() which is what we need to use whenever we receive a
|
||||
* MM_WIM_DATA. My callback would need to defer that job to another thread
|
||||
* anyway, so instead just use CALLBACK_THREAD here instead.
|
||||
*************************************************************************/
|
||||
|
||||
static bool_t getbuffer(WAVEHDR *pwh) {
|
||||
GAudioData *paud;
|
||||
|
||||
// Get the next data block to send
|
||||
gfxSystemLock();
|
||||
paud = gaudioRecordGetFreeBlockI();
|
||||
if (!paud && !nQueuedBuffers)
|
||||
gaudioRecordDoneI();
|
||||
gfxSystemUnlock();
|
||||
if (!paud)
|
||||
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->size;
|
||||
pwh->dwFlags = 0;
|
||||
if (waveInPrepareHeader(ah, pwh, sizeof(WAVEHDR))) {
|
||||
fprintf(stderr, "GAUDIO: Failed to prepare a record buffer");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Send it to windows
|
||||
if (waveInAddBuffer(ah, pwh, sizeof(WAVEHDR))) {
|
||||
fprintf(stderr, "GAUDIO: Failed to add the record buffer");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
nQueuedBuffers++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static DWORD WINAPI waveProc(LPVOID arg) {
|
||||
MSG msg;
|
||||
WAVEHDR *pwh;
|
||||
GAudioData *paud;
|
||||
(void) arg;
|
||||
|
||||
while (GetMessage(&msg, 0, 0, 0)) {
|
||||
switch (msg.message) {
|
||||
case MM_WIM_DATA:
|
||||
pwh = (WAVEHDR *)msg.lParam;
|
||||
|
||||
// Windows - Let go!
|
||||
waveInUnprepareHeader(ah, pwh, sizeof(WAVEHDR));
|
||||
|
||||
// Save the buffer in the audio record list
|
||||
paud = (GAudioData *)pwh->dwUser;
|
||||
paud->len = pwh->dwBytesRecorded;
|
||||
gfxSystemLock();
|
||||
gaudioRecordSaveDataBlockI(paud);
|
||||
gfxSystemUnlock();
|
||||
pwh->lpData = 0;
|
||||
nQueuedBuffers--;
|
||||
|
||||
// Are we stopping?
|
||||
if (!isRunning) {
|
||||
// Have we finished yet?
|
||||
if (!nQueuedBuffers) {
|
||||
gfxSystemLock();
|
||||
gaudioRecordDoneI();
|
||||
gfxSystemUnlock();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Try and get a new block
|
||||
getbuffer(pwh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
bool_t gaudio_record_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format) {
|
||||
WAVEFORMATEX wfx;
|
||||
|
||||
if (format != ARRAY_DATA_8BITUNSIGNED && format != ARRAY_DATA_16BITSIGNED)
|
||||
return FALSE;
|
||||
|
||||
if (!waveThread) {
|
||||
if (!(waveThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)waveProc, 0, 0, &threadID))) {
|
||||
fprintf(stderr, "GAUDIO: Can't create WAVE recording thread\n");
|
||||
exit(-1);
|
||||
}
|
||||
CloseHandle(waveThread);
|
||||
}
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = channel == GAUDIO_RECORD_STEREO ? 2 : 1;
|
||||
wfx.nSamplesPerSec = frequency;
|
||||
wfx.nBlockAlign = wfx.nChannels * (format == ARRAY_DATA_8BITUNSIGNED ? 1 : 2);
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
wfx.wBitsPerSample = (format == ARRAY_DATA_8BITUNSIGNED ? 8 : 16);
|
||||
wfx.cbSize = 0;
|
||||
|
||||
if (ah) {
|
||||
waveInClose(ah);
|
||||
ah = 0;
|
||||
}
|
||||
if (waveInOpen(&ah, WAVE_MAPPER, &wfx, (DWORD_PTR)threadID, 0, CALLBACK_THREAD)) {
|
||||
fprintf(stderr, "GAUDIN: Can't open WAVE recording device\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void gaudio_record_lld_start(void) {
|
||||
WAVEHDR *pwh;
|
||||
|
||||
if (!ah)
|
||||
return;
|
||||
|
||||
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 free-list
|
||||
if (!getbuffer(pwh))
|
||||
break;
|
||||
}
|
||||
if (!isRunning) {
|
||||
isRunning = TRUE;
|
||||
waveInStart(ah);
|
||||
}
|
||||
}
|
||||
|
||||
void gaudio_record_lld_stop(void) {
|
||||
isRunning = FALSE;
|
||||
waveInReset(ah);
|
||||
while(nQueuedBuffers) Sleep(1);
|
||||
}
|
||||
|
||||
#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_RECORD */
|
|
@ -0,0 +1,10 @@
|
|||
This driver uses the Win32 audio system to provide GAUDIO play and record channels.
|
||||
|
||||
For PLAY - It supports 2 channels, Channel 0 being a mono channel and Channel 1 being a stereo channel.
|
||||
For RECORD - 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.
|
|
@ -0,0 +1,5 @@
|
|||
# List the required driver.
|
||||
GFXSRC += $(GFXLIB)/drivers/gaudio/gadc/gaudio_record_lld.c
|
||||
|
||||
# Required include directories
|
||||
GFXINC += $(GFXLIB)/drivers/gaudio/gadc
|
|
@ -6,15 +6,15 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* @file drivers/gaudin/gadc/gaudin_lld_board_template.h
|
||||
* @brief GAUDIN Driver board config board file
|
||||
* @file drivers/gaudio/gadc/gaudio_record_board_template.h
|
||||
* @brief GAUDIO Record Driver board config board file
|
||||
*
|
||||
* @addtogroup GAUDIN
|
||||
* @addtogroup GAUDIO
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _GAUDIN_LLD_BOARD_H
|
||||
#define _GAUDIN_LLD_BOARD_H
|
||||
#ifndef _GAUDIO_RECORD_BOARD_H
|
||||
#define _GAUDIO_RECORD_BOARD_H
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Audio inputs on this board */
|
||||
|
@ -24,14 +24,14 @@
|
|||
* @brief The number of audio channels supported by this driver
|
||||
* @note This is an example
|
||||
*/
|
||||
#define GAUDIN_NUM_CHANNELS 1
|
||||
#define GAUDIO_RECORD_NUM_CHANNELS 1
|
||||
|
||||
/**
|
||||
* @brief The list of audio channels and their uses
|
||||
* @note This is an example
|
||||
* @{
|
||||
*/
|
||||
#define GAUDIN_MICROPHONE 0
|
||||
#define GAUDIO_RECORD_MICROPHONE 0
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -39,12 +39,12 @@
|
|||
* @note This is an example
|
||||
* @{
|
||||
*/
|
||||
#ifdef GAUDIN_LLD_IMPLEMENTATION
|
||||
static uint32_t gaudin_lld_physdevs[GAUDIN_NUM_CHANNELS] = {
|
||||
#ifdef GAUDIO_RECORD_LLD_IMPLEMENTATION
|
||||
static uint32_t gaudin_lld_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = {
|
||||
GADC_PHYSDEV_MICROPHONE,
|
||||
};
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
#endif /* _GAUDIN_LLD_BOARD_H */
|
||||
#endif /* _GAUDIO_RECORD_BOARD_H */
|
||||
/** @} */
|