This source file includes following definitions.
- WAV_Setup
- WaveProc
- close_waveform
- WAV_Shutdown
- WAV_ConfigureOutput
- WAV_WriteAudio
- WAV_Play
- set_vol_pan
- WAV_SetVolume
- WAV_SetPan
- WAV_SetVolume
- WAV_SetPan
- WAV_QueryOutputSampleRate
- WAV_GetAudioDelay
- WAV_GetTotalBufferTime
- NewWAVRender
- DeleteWAVRender
- QueryInterfaces
- LoadInterface
- ShutdownInterface
#include <gpac/modules/audio_out.h>
#include <windows.h>
#define MAX_AUDIO_BUFFER 30
typedef struct
{
HWAVEOUT hwo;
WAVEHDR wav_hdr[MAX_AUDIO_BUFFER];
WAVEFORMATEX fmt;
u32 num_buffers;
u32 buffer_size;
Bool exit_request;
HANDLE event;
u32 vol, pan;
u32 delay, total_length_ms;
Bool force_config;
u32 cfg_num_buffers, cfg_duration;
char *wav_buf;
} WAVContext;
#if !defined(DISABLE_WAVE_EX) && !defined(_WIN32_WCE)
#define USE_WAVE_EXT
#endif
#ifdef USE_WAVE_EXT
#include <gpac/constants.h>
#ifndef WAVE_FORMAT_EXTENSIBLE
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
#endif
#ifndef SPEAKER_FRONT_LEFT
# define SPEAKER_FRONT_LEFT 0x1
# define SPEAKER_FRONT_RIGHT 0x2
# define SPEAKER_FRONT_CENTER 0x4
# define SPEAKER_LOW_FREQUENCY 0x8
# define SPEAKER_BACK_LEFT 0x10
# define SPEAKER_BACK_RIGHT 0x20
# define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
# define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
# define SPEAKER_BACK_CENTER 0x100
# define SPEAKER_SIDE_LEFT 0x200
# define SPEAKER_SIDE_RIGHT 0x400
# define SPEAKER_TOP_CENTER 0x800
# define SPEAKER_TOP_FRONT_LEFT 0x1000
# define SPEAKER_TOP_FRONT_CENTER 0x2000
# define SPEAKER_TOP_FRONT_RIGHT 0x4000
# define SPEAKER_TOP_BACK_LEFT 0x8000
# define SPEAKER_TOP_BACK_CENTER 0x10000
# define SPEAKER_TOP_BACK_RIGHT 0x20000
# define SPEAKER_RESERVED 0x80000000
#endif
#ifndef _WAVEFORMATEXTENSIBLE_
typedef struct {
WAVEFORMATEX Format;
union {
WORD wValidBitsPerSample;
WORD wSamplesPerBlock;
WORD wReserved;
} Samples;
DWORD dwChannelMask;
GUID SubFormat;
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
#endif
const static GUID GPAC_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71} };
#pragma message("Using multichannel audio extensions")
#endif
#define WAVCTX() WAVContext *ctx = (WAVContext *)dr->opaque;
static GF_Err WAV_Setup(GF_AudioOutput *dr, void *os_handle, u32 num_buffers, u32 total_duration)
{
WAVCTX();
ctx->force_config = (num_buffers && total_duration) ? GF_TRUE : GF_FALSE;
ctx->cfg_num_buffers = num_buffers;
if (ctx->cfg_num_buffers <= 1) ctx->cfg_num_buffers = 2;
ctx->cfg_duration = total_duration;
if (!ctx->force_config) ctx->num_buffers = 6;
return GF_OK;
}
static void CALLBACK WaveProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
GF_AudioOutput *dr = (GF_AudioOutput *) dwInstance;
WAVCTX();
if (uMsg != WOM_DONE) return;
if (ctx->exit_request) return;
SetEvent(ctx->event);
}
static void close_waveform(GF_AudioOutput *dr)
{
WAVCTX();
if (!ctx->event) return;
#ifdef _WIN32_WCE
ctx->exit_request = GF_TRUE;
SetEvent(ctx->event);
waveOutReset(ctx->hwo);
waveOutClose(ctx->hwo);
if (ctx->wav_buf) gf_free(ctx->wav_buf);
ctx->wav_buf = NULL;
CloseHandle(ctx->event);
ctx->event = NULL;
ctx->exit_request = GF_FALSE;
#else
ctx->exit_request = GF_TRUE;
SetEvent(ctx->event);
if (ctx->hwo) {
u32 i;
Bool not_done;
MMRESULT res;
while (1) {
not_done = GF_FALSE;
for (i=0 ; i< ctx->num_buffers; i++) {
if (! (ctx->wav_hdr[i].dwFlags & WHDR_DONE)) {
not_done = GF_TRUE;
break;
}
}
if (!not_done) break;
gf_sleep(60);
}
while (1) {
res = waveOutClose(ctx->hwo);
if (res == MMSYSERR_NOERROR) break;
}
ctx->hwo = NULL;
}
if (ctx->wav_buf) gf_free(ctx->wav_buf);
ctx->wav_buf = NULL;
CloseHandle(ctx->event);
ctx->event = NULL;
ctx->exit_request = GF_FALSE;
#endif
}
static void WAV_Shutdown(GF_AudioOutput *dr)
{
close_waveform(dr);
}
static GF_Err WAV_ConfigureOutput(GF_AudioOutput *dr, u32 *SampleRate, u32 *NbChannels, u32 *nbBitsPerSample, u32 channel_cfg)
{
u32 i, retry;
HRESULT hr;
WAVEFORMATEX *fmt;
#ifdef USE_WAVE_EXT
WAVEFORMATEXTENSIBLE format_ex;
#endif
WAVCTX();
if (!ctx) return GF_BAD_PARAM;
close_waveform(dr);
#ifndef USE_WAVE_EXT
if (*NbChannels>2) *NbChannels=2;
#endif
memset (&ctx->fmt, 0, sizeof(ctx->fmt));
ctx->fmt.cbSize = sizeof(WAVEFORMATEX);
ctx->fmt.wFormatTag = WAVE_FORMAT_PCM;
ctx->fmt.nChannels = *NbChannels;
ctx->fmt.wBitsPerSample = *nbBitsPerSample;
ctx->fmt.nSamplesPerSec = *SampleRate;
ctx->fmt.nBlockAlign = ctx->fmt.wBitsPerSample * ctx->fmt.nChannels / 8;
ctx->fmt.nAvgBytesPerSec = *SampleRate * ctx->fmt.nBlockAlign;
fmt = &ctx->fmt;
#ifdef USE_WAVE_EXT
if (channel_cfg && ctx->fmt.nChannels>2) {
memset(&format_ex, 0, sizeof(WAVEFORMATEXTENSIBLE));
format_ex.Format = ctx->fmt;
format_ex.Format.cbSize = 22;
format_ex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
format_ex.SubFormat = GPAC_KSDATAFORMAT_SUBTYPE_PCM;
format_ex.Samples.wValidBitsPerSample = ctx->fmt.wBitsPerSample;
format_ex.dwChannelMask = 0;
if (channel_cfg & GF_AUDIO_CH_FRONT_LEFT) format_ex.dwChannelMask |= SPEAKER_FRONT_LEFT;
if (channel_cfg & GF_AUDIO_CH_FRONT_RIGHT) format_ex.dwChannelMask |= SPEAKER_FRONT_RIGHT;
if (channel_cfg & GF_AUDIO_CH_FRONT_CENTER) format_ex.dwChannelMask |= SPEAKER_FRONT_CENTER;
if (channel_cfg & GF_AUDIO_CH_LFE) format_ex.dwChannelMask |= SPEAKER_LOW_FREQUENCY;
if (channel_cfg & GF_AUDIO_CH_BACK_LEFT) format_ex.dwChannelMask |= SPEAKER_BACK_LEFT;
if (channel_cfg & GF_AUDIO_CH_BACK_RIGHT) format_ex.dwChannelMask |= SPEAKER_BACK_RIGHT;
if (channel_cfg & GF_AUDIO_CH_BACK_CENTER) format_ex.dwChannelMask |= SPEAKER_BACK_CENTER;
if (channel_cfg & GF_AUDIO_CH_SIDE_LEFT) format_ex.dwChannelMask |= SPEAKER_SIDE_LEFT;
if (channel_cfg & GF_AUDIO_CH_SIDE_RIGHT) format_ex.dwChannelMask |= SPEAKER_SIDE_RIGHT;
fmt = (WAVEFORMATEX *) &format_ex;
}
#endif
retry = 10;
while (retry) {
hr = waveOutOpen((LPHWAVEOUT)&ctx->hwo, WAVE_MAPPER, &ctx->fmt, (DWORD_PTR) WaveProc, (DWORD_PTR) dr,
CALLBACK_FUNCTION | WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT
);
if (hr == MMSYSERR_NOERROR) break;
if (hr != MMSYSERR_ALLOCATED) return GF_IO_ERR;
retry--;
}
if (hr != MMSYSERR_NOERROR) return GF_IO_ERR;
if (!ctx->force_config) {
ctx->buffer_size = 1024 * ctx->fmt.nBlockAlign;
ctx->num_buffers = 2;
} else {
ctx->num_buffers = ctx->cfg_num_buffers;
ctx->buffer_size = (ctx->fmt.nAvgBytesPerSec * ctx->cfg_duration) / (1000 * ctx->cfg_num_buffers);
}
ctx->event = CreateEvent( NULL, FALSE, FALSE, NULL);
while (ctx->buffer_size % ctx->fmt.nBlockAlign) ctx->buffer_size++;
ctx->wav_buf = (char*)gf_malloc(ctx->buffer_size*ctx->num_buffers*sizeof(char));
memset(ctx->wav_buf, 0, ctx->buffer_size*ctx->num_buffers*sizeof(char));
for (i=0 ; i < ctx->num_buffers; i++) {
memset(& ctx->wav_hdr[i], 0, sizeof(WAVEHDR));
ctx->wav_hdr[i].dwBufferLength = ctx->buffer_size;
ctx->wav_hdr[i].lpData = & ctx->wav_buf[i*ctx->buffer_size];
ctx->wav_hdr[i].dwFlags = WHDR_DONE;
waveOutPrepareHeader(ctx->hwo, &ctx->wav_hdr[i], sizeof(WAVEHDR));
waveOutWrite(ctx->hwo, &ctx->wav_hdr[i], sizeof(WAVEHDR));
Sleep(1);
}
ctx->total_length_ms = 1000 * ctx->num_buffers * ctx->buffer_size / ctx->fmt.nAvgBytesPerSec;
ctx->delay = ctx->total_length_ms;
return GF_OK;
}
static void WAV_WriteAudio(GF_AudioOutput *dr)
{
LPWAVEHDR hdr;
HRESULT hr;
u32 i;
WAVCTX();
if (!ctx->hwo) return;
WaitForSingleObject(ctx->event, INFINITE);
if (ctx->exit_request) return;
for (i=0; i<ctx->num_buffers; i++) {
hdr = &ctx->wav_hdr[i];
if (hdr->dwFlags & WHDR_DONE) {
waveOutUnprepareHeader(ctx->hwo, hdr, sizeof(WAVEHDR));
hdr->dwBufferLength = ctx->buffer_size;
ctx->delay = 1000 * i * ctx->buffer_size / ctx->fmt.nAvgBytesPerSec;
hdr->dwBufferLength = dr->FillBuffer(dr->audio_renderer, hdr->lpData, ctx->buffer_size);
hdr->dwFlags = 0;
hr = waveOutPrepareHeader(ctx->hwo, hdr, sizeof(WAVEHDR));
waveOutWrite(ctx->hwo, hdr, sizeof(WAVEHDR));
}
}
}
static void WAV_Play(GF_AudioOutput *dr, u32 PlayType)
{
#if 0
u32 i;
WAVCTX();
switch (PlayType) {
case 0:
waveOutPause(ctx->hwo);
break;
case 2:
for (i=0; i<ctx->num_buffers; i++) {
LPWAVEHDR hdr = &ctx->wav_hdr[i];
memset(&hdr->lpData, 0, sizeof(char)*ctx->buffer_size);
}
case 1:
waveOutRestart(ctx->hwo);
break;
}
#endif
}
#ifndef _WIN32_WCE
static void set_vol_pan(WAVContext *ctx)
{
WORD rV, lV;
lV = (WORD) (ctx->vol * ctx->pan / 100);
rV = (WORD) (ctx->vol * (100 - ctx->pan) / 100);
waveOutSetVolume(ctx->hwo, MAKELONG(lV, rV) );
}
static void WAV_SetVolume(GF_AudioOutput *dr, u32 Volume)
{
WAVCTX();
if (Volume > 100) Volume = 100;
ctx->vol = Volume * 0xFFFF / 100;
set_vol_pan(ctx);
}
static void WAV_SetPan(GF_AudioOutput *dr, u32 Pan)
{
WAVCTX();
if (Pan > 100) Pan = 100;
ctx->pan = Pan;
set_vol_pan(ctx);
}
#else
static void WAV_SetVolume(GF_AudioOutput *dr, u32 Volume) { }
static void WAV_SetPan(GF_AudioOutput *dr, u32 Pan) { }
#endif
static GF_Err WAV_QueryOutputSampleRate(GF_AudioOutput *dr, u32 *desired_samplerate, u32 *NbChannels, u32 *nbBitsPerSample)
{
#ifdef _WIN32_WCE
switch (*desired_samplerate) {
case 11025:
case 22050:
*desired_samplerate = 22050;
break;
case 8000:
case 16000:
case 32000:
*desired_samplerate = 44100;
break;
case 24000:
case 48000:
*desired_samplerate = 44100;
break;
case 44100:
*desired_samplerate = 44100;
break;
default:
break;
}
return GF_OK;
#else
return GF_OK;
#endif
}
static u32 WAV_GetAudioDelay(GF_AudioOutput *dr)
{
WAVCTX();
return ctx->delay;
}
static u32 WAV_GetTotalBufferTime(GF_AudioOutput *dr)
{
WAVCTX();
return ctx->total_length_ms;
}
void *NewWAVRender()
{
GF_AudioOutput *driv;
WAVContext *ctx = (WAVContext*)gf_malloc(sizeof(WAVContext));
memset(ctx, 0, sizeof(WAVContext));
ctx->num_buffers = 10;
ctx->pan = 50;
ctx->vol = 100;
driv = (GF_AudioOutput*)gf_malloc(sizeof(GF_AudioOutput));
memset(driv, 0, sizeof(GF_AudioOutput));
GF_REGISTER_MODULE_INTERFACE(driv, GF_AUDIO_OUTPUT_INTERFACE, "Windows MME Output", "gpac distribution")
driv->opaque = ctx;
driv->SelfThreaded = GF_FALSE;
driv->Setup = WAV_Setup;
driv->Shutdown = WAV_Shutdown;
driv->ConfigureOutput = WAV_ConfigureOutput;
driv->GetAudioDelay = WAV_GetAudioDelay;
driv->GetTotalBufferTime = WAV_GetTotalBufferTime;
driv->SetVolume = WAV_SetVolume;
driv->SetPan = WAV_SetPan;
driv->Play = WAV_Play;
driv->QueryOutputSampleRate = WAV_QueryOutputSampleRate;
driv->WriteAudio = WAV_WriteAudio;
return driv;
}
void DeleteWAVRender(void *ifce)
{
GF_AudioOutput *dr = (GF_AudioOutput *) ifce;
WAVContext *ctx = (WAVContext *)dr->opaque;
gf_free(ctx);
gf_free(dr);
}
GPAC_MODULE_EXPORT
const u32 *QueryInterfaces()
{
static u32 si [] = {
GF_AUDIO_OUTPUT_INTERFACE,
0
};
return si;
}
GPAC_MODULE_EXPORT
GF_BaseInterface *LoadInterface(u32 InterfaceType)
{
if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE)
return (GF_BaseInterface*)NewWAVRender();
return NULL;
}
GPAC_MODULE_EXPORT
void ShutdownInterface(GF_BaseInterface *ifce)
{
switch (ifce->InterfaceType) {
case GF_AUDIO_OUTPUT_INTERFACE:
DeleteWAVRender((GF_AudioOutput *) ifce);
break;
}
}
GPAC_MODULE_STATIC_DECLARATION( wave_out )