📄 wav_out.c
字号:
/* * GPAC - Multimedia Framework C SDK * * Copyright (c) Jean Le Feuvre 2000-2005 * All rights reserved * * This file is part of GPAC / wave audio render module * * GPAC is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * GPAC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <gpac/modules/audio_out.h>#include <windows.h>#define MAX_AUDIO_BUFFER 30typedef 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)/*IF YOU CAN'T COMPILE WAVEOUT TRY TO COMMENT THIS - note waveOut & multichannel is likely not to work*/#define USE_WAVE_EXT#endif#ifdef USE_WAVE_EXT/*for channel codes*/#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; /* bits of precision */ WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */ WORD wReserved; /* If neither applies, set to zero. */ } Samples; DWORD dwChannelMask; /* which channels are */ /* present in stream */ GUID SubFormat;} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;#endifconst 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) ? 1 : 0; 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 dwInstance, DWORD dwParam1, DWORD 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; /*brute-force version, actually much safer on winCE*/#ifdef _WIN32_WCE ctx->exit_request = 1; SetEvent(ctx->event); waveOutReset(ctx->hwo); waveOutClose(ctx->hwo); if (ctx->wav_buf) free(ctx->wav_buf); ctx->wav_buf = NULL; CloseHandle(ctx->event); ctx->event = NULL; ctx->exit_request = 0;#else ctx->exit_request = 1; SetEvent(ctx->event); if (ctx->hwo) { u32 i; Bool not_done; MMRESULT res; /*wait for all buffers to complete, otherwise this locks waveOutReset*/ while (1) { not_done = 0; for (i=0 ; i< ctx->num_buffers; i++) { if (! (ctx->wav_hdr[i].dwFlags & WHDR_DONE)) { not_done = 1; break; } } if (!not_done) break; gf_sleep(60); } /*waveOutReset gives unpredictable results on PocketPC, so just close right away*/ while (1) { res = waveOutClose(ctx->hwo); if (res == MMSYSERR_NOERROR) break; } ctx->hwo = NULL; } if (ctx->wav_buf) free(ctx->wav_buf); ctx->wav_buf = NULL; CloseHandle(ctx->event); ctx->event = NULL; ctx->exit_request = 0;#endif}static void WAV_Shutdown(GF_AudioOutput *dr){ close_waveform(dr);}/*we assume what was asked is what we got*/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; /*reset*/ 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 /* Open a waveform device for output using window callback. */ retry = 10; while (retry) { hr = waveOutOpen((LPHWAVEOUT)&ctx->hwo, WAVE_MAPPER, &ctx->fmt, (DWORD) WaveProc, (DWORD) dr, CALLBACK_FUNCTION | WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT ); if (hr == MMSYSERR_NOERROR) break; /*couldn't open audio*/ if (hr != MMSYSERR_ALLOCATED) return GF_IO_ERR; retry--; } if (hr != MMSYSERR_NOERROR) return GF_IO_ERR; if (!ctx->force_config) { /*one wave buffer size*/ 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); /*make sure we're aligned*/ while (ctx->buffer_size % ctx->fmt.nBlockAlign) ctx->buffer_size++; ctx->wav_buf = malloc(ctx->buffer_size*ctx->num_buffers*sizeof(char)); memset(ctx->wav_buf, 0, ctx->buffer_size*ctx->num_buffers*sizeof(char)); /*setup wave headers*/ 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; /*initial delay is full buffer size*/ 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++) { /*get buffer*/ hdr = &ctx->wav_hdr[i]; if (hdr->dwFlags & WHDR_DONE) { waveOutUnprepareHeader(ctx->hwo, hdr, sizeof(WAVEHDR)); hdr->dwBufferLength = ctx->buffer_size; /*update delay*/ ctx->delay = 1000 * i * ctx->buffer_size / ctx->fmt.nAvgBytesPerSec; /*fill it*/ hdr->dwBufferLength = dr->FillBuffer(dr->audio_renderer, hdr->lpData, ctx->buffer_size); hdr->dwFlags = 0; hr = waveOutPrepareHeader(ctx->hwo, hdr, sizeof(WAVEHDR)); /*write it*/ 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_WCEstatic void set_vol_pan(WAVContext *ctx){ WORD rV, lV; /*in wave, volume & pan are specified as a DWORD. LOW word is LEFT channel, HIGH is right - iPaq doesn't support that*/ 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/*too much trouble with iPaq ...*/static void WAV_SetVolume(GF_AudioOutput *dr, u32 Volume) { }static void WAV_SetPan(GF_AudioOutput *dr, u32 Pan) { }#endifstatic GF_Err WAV_QueryOutputSampleRate(GF_AudioOutput *dr, u32 *desired_samplerate, u32 *NbChannels, u32 *nbBitsPerSample){ /*iPaq's output frequencies available*/#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(){ WAVContext *ctx; GF_AudioOutput *driv; ctx = malloc(sizeof(WAVContext)); memset(ctx, 0, sizeof(WAVContext)); ctx->num_buffers = 10; ctx->pan = 50; ctx->vol = 100; driv = 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 = 0; 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; free(ctx); free(dr);}Bool QueryInterface(u32 InterfaceType){ if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return 1; return 0;}GF_BaseInterface *LoadInterface(u32 InterfaceType){ if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return NewWAVRender(); return NULL;}void ShutdownInterface(GF_BaseInterface *ifce){ switch (ifce->InterfaceType) { case GF_AUDIO_OUTPUT_INTERFACE: DeleteWAVRender((GF_AudioOutput *) ifce); break; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -