forked from mirrors/linphone-iphone
git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@1 3f6dc0c8-ddfe-455d-9043-3cd528dc4637
845 lines
22 KiB
C++
845 lines
22 KiB
C++
/*
|
|
mediastreamer2 library - modular sound and video processing and streaming
|
|
Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef __DIRECTSOUND_ENABLED__
|
|
|
|
#include "mediastreamer2/mssndcard.h"
|
|
#include "mediastreamer2/msfilter.h"
|
|
#include "mediastreamer2/msticker.h"
|
|
|
|
#include <mmsystem.h>
|
|
#ifdef _MSC_VER
|
|
#include <mmreg.h>
|
|
#endif
|
|
#include <msacm.h>
|
|
|
|
#include <dsound.h>
|
|
|
|
#define WINSNDDS_NBUFS 10
|
|
#define WINSNDDS_NSAMPLES 160
|
|
#define WINSNDDS_MINIMUMBUFFER 5
|
|
|
|
static MSFilter *ms_winsndds_read_new(MSSndCard *card);
|
|
static MSFilter *ms_winsndds_write_new(MSSndCard *card);
|
|
|
|
static HMODULE ms_lib_instance=NULL;
|
|
static HRESULT (WINAPI *ms_DllGetClassObject)(REFCLSID , REFIID , LPVOID *);
|
|
|
|
static HRESULT (WINAPI *ms_DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
|
|
static HRESULT (WINAPI *ms_DirectSoundEnumerate)(LPDSENUMCALLBACKA, LPVOID);
|
|
|
|
static HRESULT (WINAPI *ms_DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
|
|
static HRESULT (WINAPI *ms_DirectSoundCaptureEnumerate)(LPDSENUMCALLBACKA, LPVOID);
|
|
|
|
typedef struct WinSndDsCard{
|
|
int in_devid;
|
|
int out_devid;
|
|
GUID in_guid;
|
|
GUID out_guid;
|
|
}WinSndDsCard;
|
|
|
|
|
|
static void winsnddscard_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent){
|
|
MMRESULT mr = MMSYSERR_NOERROR;
|
|
DWORD dwVolume = 0xFFFF;
|
|
dwVolume = ((0xFFFF) * percent) / 100;
|
|
|
|
switch(e){
|
|
case MS_SND_CARD_MASTER:
|
|
/*mr = waveOutSetVolume(d->waveoutdev, dwVolume); */
|
|
if (mr != MMSYSERR_NOERROR)
|
|
{
|
|
ms_warning("Failed to set master volume. (waveOutSetVolume:0x%i)", mr);
|
|
return;
|
|
}
|
|
break;
|
|
case MS_SND_CARD_CAPTURE:
|
|
break;
|
|
case MS_SND_CARD_PLAYBACK:
|
|
break;
|
|
default:
|
|
ms_warning("winsndds_card_set_level: unsupported command.");
|
|
}
|
|
}
|
|
|
|
static int winsnddscard_get_level(MSSndCard *card, MSSndCardMixerElem e){
|
|
switch(e){
|
|
case MS_SND_CARD_MASTER:
|
|
/*mr=waveOutGetVolume(d->waveoutdev, &dwVolume);*/
|
|
/* Transform to 0 to 100 scale*/
|
|
/*dwVolume = (dwVolume *100) / (0xFFFF);*/
|
|
return 60;
|
|
break;
|
|
case MS_SND_CARD_CAPTURE:
|
|
break;
|
|
case MS_SND_CARD_PLAYBACK:
|
|
break;
|
|
default:
|
|
ms_warning("winsndds_card_get_level: unsupported command.");
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void winsnddscard_set_source(MSSndCard *card, MSSndCardCapture source){
|
|
|
|
switch(source){
|
|
case MS_SND_CARD_MIC:
|
|
break;
|
|
case MS_SND_CARD_LINE:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void winsnddscard_init(MSSndCard *card){
|
|
WinSndDsCard *c=(WinSndDsCard *)ms_new(WinSndDsCard,1);
|
|
card->data=c;
|
|
}
|
|
|
|
static void winsnddscard_uninit(MSSndCard *card){
|
|
ms_free(card->data);
|
|
}
|
|
|
|
static void winsnddscard_detect(MSSndCardManager *m);
|
|
static MSSndCard *winsnddscard_dup(MSSndCard *obj);
|
|
|
|
MSSndCardDesc winsndds_card_desc={
|
|
"WINSNDDS",
|
|
winsnddscard_detect,
|
|
winsnddscard_init,
|
|
winsnddscard_set_level,
|
|
winsnddscard_get_level,
|
|
winsnddscard_set_source,
|
|
ms_winsndds_read_new,
|
|
ms_winsndds_write_new,
|
|
winsnddscard_uninit,
|
|
winsnddscard_dup
|
|
};
|
|
|
|
static MSSndCard *winsnddscard_dup(MSSndCard *obj){
|
|
MSSndCard *card=ms_snd_card_new(&winsndds_card_desc);
|
|
card->name=ms_strdup(obj->name);
|
|
card->data=ms_new(WinSndDsCard,1);
|
|
memcpy(card->data,obj->data,sizeof(WinSndDsCard));
|
|
return card;
|
|
}
|
|
|
|
static MSSndCard *winsnddscard_new(const char *name, LPGUID lpguid, int in_dev, int out_dev, unsigned cap){
|
|
MSSndCard *card=ms_snd_card_new(&winsndds_card_desc);
|
|
WinSndDsCard *d=(WinSndDsCard*)card->data;
|
|
card->name=ms_strdup(name);
|
|
d->in_devid=in_dev;
|
|
d->out_devid=out_dev;
|
|
card->capabilities=cap;
|
|
if (out_dev!=-1)
|
|
{
|
|
if (lpguid!=NULL)
|
|
memcpy(&d->out_guid, lpguid, sizeof(GUID));
|
|
else
|
|
memset(&d->out_guid, 0, sizeof(GUID));
|
|
}
|
|
else
|
|
{
|
|
if (lpguid!=NULL)
|
|
memcpy(&d->in_guid, lpguid, sizeof(GUID));
|
|
else
|
|
memset(&d->in_guid, 0, sizeof(GUID));
|
|
}
|
|
return card;
|
|
}
|
|
|
|
static void add_or_update_card(MSSndCardManager *m, const char *name, LPGUID lpguid, int indev, int outdev, unsigned int capability){
|
|
MSSndCard *card;
|
|
const MSList *elem=ms_snd_card_manager_get_list(m);
|
|
for(;elem!=NULL;elem=elem->next){
|
|
card=(MSSndCard*)elem->data;
|
|
if (strcmp(card->name,name)==0){
|
|
/*update already entered card */
|
|
WinSndDsCard *d=(WinSndDsCard*)card->data;
|
|
card->capabilities|=capability;
|
|
if (indev!=-1)
|
|
d->in_devid=indev;
|
|
if (outdev!=-1)
|
|
d->out_devid=outdev;
|
|
|
|
if (outdev!=-1)
|
|
{
|
|
if (lpguid!=NULL)
|
|
memcpy(&d->out_guid, lpguid, sizeof(GUID));
|
|
else
|
|
memset(&d->out_guid, 0, sizeof(GUID));
|
|
}
|
|
if (indev!=-1)
|
|
{
|
|
if (lpguid!=NULL)
|
|
memcpy(&d->in_guid, lpguid, sizeof(GUID));
|
|
else
|
|
memset(&d->in_guid, 0, sizeof(GUID));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
/* add this new card:*/
|
|
ms_snd_card_manager_add_card(m,winsnddscard_new(name,lpguid, indev,outdev,capability));
|
|
}
|
|
|
|
static BOOL CALLBACK enumerate_capture_devices_callback(LPGUID lpGUID,
|
|
LPCTSTR lpszDesc,
|
|
LPCTSTR lpszDrvName,
|
|
LPVOID lpContext )
|
|
{
|
|
MSSndCardManager *m = (MSSndCardManager*)lpContext;
|
|
static int dev_index=0;
|
|
|
|
if ( lpGUID == NULL ) /* primary device */
|
|
{
|
|
char snd_card_name[256];
|
|
snprintf(snd_card_name, 256, "ds: %s", lpszDesc);
|
|
add_or_update_card(m,snd_card_name,lpGUID,dev_index,-1,MS_SND_CARD_CAP_CAPTURE);
|
|
dev_index++;
|
|
}
|
|
else
|
|
{
|
|
char snd_card_name[256];
|
|
snprintf(snd_card_name, 256, "ds: %s", lpszDesc);
|
|
add_or_update_card(m,snd_card_name,lpGUID,dev_index,-1,MS_SND_CARD_CAP_CAPTURE);
|
|
dev_index++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static BOOL CALLBACK enumerate_playback_devices_callback(LPGUID lpGUID,
|
|
LPCTSTR lpszDesc,
|
|
LPCTSTR lpszDrvName,
|
|
LPVOID lpContext )
|
|
{
|
|
MSSndCardManager *m = (MSSndCardManager*)lpContext;
|
|
static int dev_index=0;
|
|
|
|
if ( lpGUID == NULL ) /* primary device */
|
|
{
|
|
char snd_card_name[256];
|
|
snprintf(snd_card_name, 256, "ds: %s", lpszDesc);
|
|
|
|
add_or_update_card(m,snd_card_name,lpGUID,-1,dev_index,MS_SND_CARD_CAP_PLAYBACK);
|
|
dev_index++;
|
|
}
|
|
else
|
|
{
|
|
char snd_card_name[256];
|
|
snprintf(snd_card_name, 256, "ds: %s", lpszDesc);
|
|
|
|
add_or_update_card(m,snd_card_name,lpGUID,-1,dev_index,MS_SND_CARD_CAP_PLAYBACK);
|
|
dev_index++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void winsnddscard_detect(MSSndCardManager *m){
|
|
MMRESULT mr = NOERROR;
|
|
|
|
if (ms_lib_instance==NULL)
|
|
{
|
|
ms_lib_instance = LoadLibrary("dsound.dll");
|
|
if( ms_lib_instance == NULL )
|
|
{
|
|
/* error */
|
|
ms_debug("winsnddscard_init: no support for dsound (missing dsound.dll)\n");
|
|
return;
|
|
}
|
|
|
|
ms_DllGetClassObject =(HRESULT (WINAPI *)(REFCLSID, REFIID , LPVOID *))
|
|
GetProcAddress( ms_lib_instance, "DllGetClassObject" );
|
|
|
|
ms_DirectSoundCreate =(HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
|
|
GetProcAddress( ms_lib_instance, "DirectSoundCreate" );
|
|
|
|
ms_DirectSoundEnumerate =(HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
|
|
GetProcAddress( ms_lib_instance, "DirectSoundEnumerateA" );
|
|
|
|
ms_DirectSoundCaptureCreate =(HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN))
|
|
GetProcAddress( ms_lib_instance, "DirectSoundCaptureCreate" );
|
|
|
|
ms_DirectSoundCaptureEnumerate =(HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
|
|
GetProcAddress( ms_lib_instance, "DirectSoundCaptureEnumerateA" );
|
|
|
|
if( ms_DllGetClassObject == NULL ||
|
|
ms_DirectSoundCreate == NULL ||
|
|
ms_DirectSoundEnumerate == NULL ||
|
|
ms_DirectSoundCaptureEnumerate == NULL ||
|
|
ms_DirectSoundCaptureCreate == NULL )
|
|
{
|
|
/* error */
|
|
ms_debug("winsnddscard_init: no support for dsound\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
ms_DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)enumerate_capture_devices_callback, (void *)m );
|
|
ms_DirectSoundEnumerate( (LPDSENUMCALLBACK)enumerate_playback_devices_callback, (void *)m );
|
|
}
|
|
|
|
|
|
typedef struct WinSndDs{
|
|
int dev_id;
|
|
GUID in_guid;
|
|
GUID out_guid;
|
|
|
|
ms_thread_t thread;
|
|
ms_mutex_t thread_lock;
|
|
ms_cond_t thread_cond;
|
|
bool_t thread_running;
|
|
|
|
MSBufferizer output_buff;
|
|
LPDIRECTSOUND lpDirectSound;
|
|
LPDIRECTSOUNDBUFFER lpDirectSoundOutputBuffer;
|
|
DWORD outputBufferWriteOffsetBytes; /* last write position */
|
|
double dsw_framesWritten;
|
|
|
|
LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
|
|
LPDIRECTSOUNDCAPTUREBUFFER lpDirectSoundInputBuffer;
|
|
UINT readOffset; /* last read position */
|
|
UINT inputSize;
|
|
|
|
int framesPerDSBuffer;
|
|
|
|
WAVEFORMATEX wfx;
|
|
queue_t rq;
|
|
ms_mutex_t mutex;
|
|
unsigned int bytes_read;
|
|
unsigned int nbufs_playing;
|
|
|
|
int32_t stat_input;
|
|
int32_t stat_output;
|
|
int32_t stat_notplayed;
|
|
|
|
int32_t stat_minimumbuffer;
|
|
}WinSndDs;
|
|
|
|
void *
|
|
winsndds_read_thread(void *arg)
|
|
{
|
|
WinSndDs *d=(WinSndDs*)arg;
|
|
|
|
ms_mutex_lock(&d->thread_lock);
|
|
ms_cond_signal(&d->thread_cond);
|
|
ms_mutex_unlock(&d->thread_lock);
|
|
|
|
while(d->thread_running)
|
|
{
|
|
HRESULT hr;
|
|
DWORD capturePos;
|
|
DWORD readPos;
|
|
long filled = 0;
|
|
long bytesFilled = 0;
|
|
LPBYTE lpInBuf1 = NULL;
|
|
LPBYTE lpInBuf2 = NULL;
|
|
DWORD dwInSize1 = 0;
|
|
DWORD dwInSize2 = 0;
|
|
|
|
hr = IDirectSoundCaptureBuffer_GetCurrentPosition( d->lpDirectSoundInputBuffer,
|
|
&capturePos, &readPos );
|
|
if( hr != DS_OK )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
filled = readPos - d->readOffset;
|
|
if( filled < 0 ) filled += d->inputSize; // unwrap offset
|
|
bytesFilled = filled;
|
|
|
|
hr = IDirectSoundCaptureBuffer_Lock ( d->lpDirectSoundInputBuffer,
|
|
d->readOffset, bytesFilled,
|
|
(void **) &lpInBuf1, &dwInSize1,
|
|
(void **) &lpInBuf2, &dwInSize2, 0);
|
|
if (hr != DS_OK)
|
|
{
|
|
Sleep(10);
|
|
continue;
|
|
}
|
|
|
|
if (dwInSize1==0)
|
|
{
|
|
Sleep(10);
|
|
}
|
|
else if (dwInSize1>=bytesFilled)
|
|
{
|
|
mblk_t *m=allocb(bytesFilled,0);
|
|
memcpy(m->b_rptr, lpInBuf1, bytesFilled);
|
|
m->b_wptr+=bytesFilled;
|
|
ms_mutex_lock(&d->mutex);
|
|
putq(&d->rq,m);
|
|
ms_mutex_unlock(&d->mutex);
|
|
d->bytes_read+=bytesFilled;
|
|
}
|
|
else
|
|
{
|
|
mblk_t *m=allocb(bytesFilled,0);
|
|
memcpy(m->b_rptr, lpInBuf1, dwInSize1);
|
|
memcpy(m->b_rptr+dwInSize1, lpInBuf2, dwInSize2);
|
|
m->b_wptr+=bytesFilled;
|
|
ms_mutex_lock(&d->mutex);
|
|
putq(&d->rq,m);
|
|
ms_mutex_unlock(&d->mutex);
|
|
d->bytes_read+=bytesFilled;
|
|
}
|
|
|
|
d->readOffset = (d->readOffset + bytesFilled) % d->inputSize;
|
|
|
|
IDirectSoundCaptureBuffer_Unlock( d->lpDirectSoundInputBuffer,
|
|
lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
|
|
}
|
|
|
|
ms_mutex_lock(&d->thread_lock);
|
|
ms_cond_signal(&d->thread_cond);
|
|
ms_mutex_unlock(&d->thread_lock);
|
|
ms_thread_exit(NULL);
|
|
return NULL;
|
|
}
|
|
|
|
static void winsndds_apply_settings(WinSndDs *d){
|
|
d->wfx.nBlockAlign=d->wfx.nChannels*d->wfx.wBitsPerSample/8;
|
|
d->wfx.nAvgBytesPerSec=d->wfx.nSamplesPerSec*d->wfx.nBlockAlign;
|
|
}
|
|
|
|
static uint64_t winsndds_get_cur_time( void *data){
|
|
WinSndDs *d=(WinSndDs*)data;
|
|
uint64_t curtime=((uint64_t)d->bytes_read*1000)/(uint64_t)d->wfx.nAvgBytesPerSec;
|
|
/* ms_debug("winsndds_get_cur_time: bytes_read=%u return %lu\n",d->bytes_read,(unsigned long)curtime); */
|
|
return curtime;
|
|
}
|
|
|
|
|
|
static void winsndds_init(MSFilter *f){
|
|
WinSndDs *d=(WinSndDs *)ms_new0(WinSndDs,1);
|
|
d->wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
d->wfx.cbSize = 0;
|
|
d->wfx.nAvgBytesPerSec = 16000;
|
|
d->wfx.nBlockAlign = 2;
|
|
d->wfx.nChannels = 1;
|
|
d->wfx.nSamplesPerSec = 8000;
|
|
d->wfx.wBitsPerSample = 16;
|
|
qinit(&d->rq);
|
|
ms_mutex_init(&d->mutex,NULL);
|
|
f->data=d;
|
|
|
|
d->stat_input=0;
|
|
d->stat_output=0;
|
|
d->stat_notplayed=0;
|
|
d->stat_minimumbuffer=WINSNDDS_MINIMUMBUFFER;
|
|
|
|
d->framesPerDSBuffer = 4096*3; //320 * (8000 / 1000);
|
|
|
|
d->thread = NULL;
|
|
ms_mutex_init(&d->thread_lock,NULL);
|
|
ms_cond_init(&d->thread_cond,NULL);
|
|
d->thread_running = FALSE;
|
|
|
|
ms_bufferizer_init(&d->output_buff);
|
|
}
|
|
|
|
static void winsndds_uninit(MSFilter *f){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
|
|
d->thread = NULL;
|
|
d->thread_running = FALSE;
|
|
ms_cond_destroy(&d->thread_cond);
|
|
ms_mutex_destroy(&d->thread_lock);
|
|
ms_bufferizer_uninit(&d->output_buff);
|
|
|
|
flushq(&d->rq,0);
|
|
ms_mutex_destroy(&d->mutex);
|
|
ms_free(f->data);
|
|
}
|
|
|
|
static void winsndds_read_preprocess(MSFilter *f){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
DSCBUFFERDESC captureDesc;
|
|
HRESULT hr;
|
|
|
|
d->stat_input=0;
|
|
d->stat_output=0;
|
|
d->stat_notplayed=0;
|
|
d->stat_minimumbuffer=WINSNDDS_MINIMUMBUFFER;
|
|
|
|
winsndds_apply_settings(d);
|
|
ms_DirectSoundCaptureCreate( &d->in_guid, &d->lpDirectSoundCapture, NULL );
|
|
|
|
d->inputSize = d->framesPerDSBuffer * 1 * sizeof(short);
|
|
|
|
ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
|
|
captureDesc.dwSize = sizeof(DSCBUFFERDESC);
|
|
captureDesc.dwFlags = 0;
|
|
captureDesc.dwBufferBytes = d->framesPerDSBuffer * 1 * sizeof(short); //bytesPerBuffer;
|
|
captureDesc.lpwfxFormat = &d->wfx;
|
|
// Create the capture buffer
|
|
if ((hr = IDirectSoundCapture_CreateCaptureBuffer( d->lpDirectSoundCapture,
|
|
&captureDesc, &d->lpDirectSoundInputBuffer, NULL)) != DS_OK)
|
|
{
|
|
return; /* hr; */
|
|
}
|
|
d->readOffset = 0; // reset last read position to start of buffer
|
|
|
|
hr = IDirectSoundCaptureBuffer_Start( d->lpDirectSoundInputBuffer, DSCBSTART_LOOPING );
|
|
|
|
ms_ticker_set_time_func(f->ticker,winsndds_get_cur_time,d);
|
|
|
|
d->thread_running=TRUE;
|
|
ms_thread_create(&d->thread,NULL,winsndds_read_thread,d);
|
|
ms_mutex_lock(&d->thread_lock);
|
|
ms_cond_wait(&d->thread_cond,&d->thread_lock);
|
|
ms_mutex_unlock(&d->thread_lock);
|
|
|
|
return; /* DS_OK; */
|
|
}
|
|
|
|
static void winsndds_read_postprocess(MSFilter *f){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
|
|
ms_mutex_lock(&d->thread_lock);
|
|
d->thread_running=FALSE;
|
|
ms_cond_wait(&d->thread_cond,&d->thread_lock);
|
|
ms_mutex_unlock(&d->thread_lock);
|
|
ms_thread_join(d->thread,NULL);
|
|
|
|
ms_ticker_set_time_func(f->ticker,NULL,NULL);
|
|
|
|
if( d->lpDirectSoundInputBuffer )
|
|
{
|
|
IDirectSoundCaptureBuffer_Stop( d->lpDirectSoundInputBuffer );
|
|
IDirectSoundCaptureBuffer_Release( d->lpDirectSoundInputBuffer );
|
|
d->lpDirectSoundInputBuffer = NULL;
|
|
}
|
|
|
|
if( d->lpDirectSoundCapture )
|
|
{
|
|
IDirectSoundCapture_Release( d->lpDirectSoundCapture );
|
|
d->lpDirectSoundCapture = NULL;
|
|
}
|
|
|
|
ms_message("Shutting down sound device (playing: %i) (input-output: %i) (notplayed: %i)", d->nbufs_playing, d->stat_input - d->stat_output, d->stat_notplayed);
|
|
flushq(&d->rq,0);
|
|
}
|
|
|
|
static void winsndds_read_process(MSFilter *f){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
mblk_t *m;
|
|
|
|
ms_mutex_lock(&d->mutex);
|
|
while((m=getq(&d->rq))!=NULL){
|
|
ms_queue_put(f->outputs[0],m);
|
|
}
|
|
ms_mutex_unlock(&d->mutex);
|
|
}
|
|
|
|
static void winsndds_write_preprocess(MSFilter *f){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
|
|
DWORD dwDataLen;
|
|
DWORD playCursor;
|
|
HWND hWnd;
|
|
HRESULT hr;
|
|
LPDIRECTSOUNDBUFFER pPrimaryBuffer;
|
|
DSBUFFERDESC primaryDesc;
|
|
DSBUFFERDESC secondaryDesc;
|
|
unsigned char* pDSBuffData;
|
|
|
|
|
|
d->stat_input=0;
|
|
d->stat_output=0;
|
|
d->stat_notplayed=0;
|
|
d->stat_minimumbuffer=WINSNDDS_MINIMUMBUFFER;
|
|
|
|
winsndds_apply_settings(d);
|
|
|
|
|
|
ms_DirectSoundCreate( &d->out_guid, &d->lpDirectSound, NULL );
|
|
|
|
|
|
hWnd = GetDesktopWindow();
|
|
if ((hr = IDirectSound_SetCooperativeLevel( d->lpDirectSound,
|
|
hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
|
|
primaryDesc.dwSize = sizeof(DSBUFFERDESC);
|
|
primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
|
|
primaryDesc.dwBufferBytes = 0;
|
|
primaryDesc.lpwfxFormat = NULL;
|
|
// Create the buffer
|
|
if ((hr = IDirectSound_CreateSoundBuffer( d->lpDirectSound,
|
|
&primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK)
|
|
{
|
|
return ;//hr;
|
|
}
|
|
|
|
if ((hr = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &d->wfx)) != DS_OK)
|
|
{
|
|
return ;//hr;
|
|
}
|
|
|
|
// Setup the secondary buffer description
|
|
ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
|
|
secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
|
|
secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
|
secondaryDesc.dwBufferBytes = d->framesPerDSBuffer * 1 * sizeof(short); //bytesPerBuffer;
|
|
secondaryDesc.lpwfxFormat = &d->wfx;
|
|
// Create the secondary buffer
|
|
if ((hr = IDirectSound_CreateSoundBuffer( d->lpDirectSound,
|
|
&secondaryDesc, &d->lpDirectSoundOutputBuffer, NULL)) != DS_OK)
|
|
{
|
|
return ;//hr;
|
|
}
|
|
|
|
// Lock the DS buffer
|
|
if ((hr = IDirectSoundBuffer_Lock( d->lpDirectSoundOutputBuffer, 0,
|
|
d->framesPerDSBuffer * 1 * sizeof(short),
|
|
(LPVOID*)&pDSBuffData,
|
|
&dwDataLen, NULL, 0, 0)) != DS_OK)
|
|
{
|
|
return ;//hr;
|
|
}
|
|
|
|
// Zero the DS buffer
|
|
ZeroMemory(pDSBuffData, dwDataLen);
|
|
// Unlock the DS buffer
|
|
if ((hr = IDirectSoundBuffer_Unlock( d->lpDirectSoundOutputBuffer,
|
|
pDSBuffData, dwDataLen, NULL, 0)) != DS_OK)
|
|
{
|
|
return ;//hr;
|
|
}
|
|
|
|
// Let DSound set the starting write position because if we set it to zero, it looks like the
|
|
// buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
|
|
hr = IDirectSoundBuffer_GetCurrentPosition( d->lpDirectSoundOutputBuffer,
|
|
&playCursor, &d->outputBufferWriteOffsetBytes );
|
|
if( hr != DS_OK )
|
|
{
|
|
return ;//hr;
|
|
}
|
|
|
|
hr = IDirectSoundBuffer_SetCurrentPosition( d->lpDirectSoundOutputBuffer, 0 );
|
|
if( hr != DS_OK )
|
|
{
|
|
return ;//hr;
|
|
}
|
|
hr = IDirectSoundBuffer_Play( d->lpDirectSoundOutputBuffer, 0, 0, DSBPLAY_LOOPING);
|
|
if( hr != DS_OK )
|
|
{
|
|
return ;//hr;
|
|
}
|
|
|
|
return ;//hr;
|
|
}
|
|
|
|
static void winsndds_write_postprocess(MSFilter *f){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
|
|
if( d->lpDirectSoundOutputBuffer )
|
|
{
|
|
IDirectSoundBuffer_Stop( d->lpDirectSoundOutputBuffer );
|
|
IDirectSoundBuffer_Release( d->lpDirectSoundOutputBuffer );
|
|
d->lpDirectSoundOutputBuffer = NULL;
|
|
}
|
|
|
|
if( d->lpDirectSound )
|
|
{
|
|
IDirectSound_Release( d->lpDirectSound );
|
|
d->lpDirectSound = NULL;
|
|
}
|
|
|
|
ms_message("Shutting down sound device (playing: %i) (input-output: %i) (notplayed: %i)", d->nbufs_playing, d->stat_input - d->stat_output, d->stat_notplayed);
|
|
}
|
|
|
|
static void winsndds_write_process(MSFilter *f){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
int discarded=0;
|
|
static int fillme=0;
|
|
|
|
if (d->lpDirectSound==NULL) {
|
|
ms_queue_flush(f->inputs[0]);
|
|
return;
|
|
}
|
|
|
|
ms_bufferizer_put_from_queue(&d->output_buff,f->inputs[0]);
|
|
|
|
if (fillme==0)
|
|
{
|
|
if (ms_bufferizer_get_avail(&d->output_buff)>=4096*3)
|
|
{
|
|
fillme=1;
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
int msize = 4096;// 1280;
|
|
while (ms_bufferizer_get_avail(&d->output_buff)>=msize)
|
|
{
|
|
LPBYTE lpOutBuf1 = NULL;
|
|
LPBYTE lpOutBuf2 = NULL;
|
|
DWORD dwOutSize1 = 0;
|
|
DWORD dwOutSize2 = 0;
|
|
HRESULT hr;
|
|
char input[4096];
|
|
|
|
hr = IDirectSoundBuffer_Lock ( d->lpDirectSoundOutputBuffer,
|
|
d->outputBufferWriteOffsetBytes, msize, //bytesToXfer,
|
|
(void **) &lpOutBuf1, &dwOutSize1,
|
|
(void **) &lpOutBuf2, &dwOutSize2, DSBLOCK_FROMWRITECURSOR);
|
|
if (hr != DS_OK)
|
|
{
|
|
ms_error("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n", hr);
|
|
continue;
|
|
}
|
|
|
|
if (dwOutSize1==0)
|
|
{
|
|
ms_error("no free room to play sample\n");
|
|
}
|
|
else if (dwOutSize1+dwOutSize2!=msize)
|
|
{
|
|
ms_bufferizer_read(&d->output_buff,(uint8_t*)input,dwOutSize1+dwOutSize2);
|
|
memcpy(lpOutBuf1, input, dwOutSize1);
|
|
memcpy(lpOutBuf2, input+dwOutSize1, dwOutSize2);
|
|
}
|
|
else if (dwOutSize1>=msize)
|
|
{
|
|
ms_bufferizer_read(&d->output_buff,(uint8_t*)input,msize);
|
|
memcpy(lpOutBuf1, input, msize);
|
|
}
|
|
else
|
|
{
|
|
ms_bufferizer_read(&d->output_buff,(uint8_t*)input,msize);
|
|
memcpy(lpOutBuf1, input, dwOutSize1);
|
|
memcpy(lpOutBuf2, input+dwOutSize1, dwOutSize2);
|
|
}
|
|
|
|
IDirectSoundBuffer_Unlock( d->lpDirectSoundOutputBuffer,
|
|
lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
|
|
if (dwOutSize1==0)
|
|
break;
|
|
if (dwOutSize1+dwOutSize2!=msize)
|
|
break;
|
|
}
|
|
|
|
if (discarded>0)
|
|
ms_warning("Extra data for sound card removed (%i buf), (playing: %i) (input-output: %i)", discarded, d->nbufs_playing, d->stat_input - d->stat_output);
|
|
}
|
|
|
|
static int set_rate(MSFilter *f, void *arg){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
d->wfx.nSamplesPerSec=*((int*)arg);
|
|
return 0;
|
|
}
|
|
|
|
static int set_nchannels(MSFilter *f, void *arg){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
d->wfx.nChannels=*((int*)arg);
|
|
return 0;
|
|
}
|
|
|
|
static int winsndds_get_stat_input(MSFilter *f, void *arg){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
return d->stat_input;
|
|
}
|
|
|
|
static int winsndds_get_stat_ouptut(MSFilter *f, void *arg){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
|
|
return d->stat_output;
|
|
}
|
|
|
|
static int winsndds_get_stat_discarded(MSFilter *f, void *arg){
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
|
|
return d->stat_notplayed;
|
|
}
|
|
|
|
static MSFilterMethod winsndds_methods[]={
|
|
{ MS_FILTER_SET_SAMPLE_RATE , set_rate },
|
|
{ MS_FILTER_SET_NCHANNELS , set_nchannels },
|
|
{ MS_FILTER_GET_STAT_INPUT, winsndds_get_stat_input },
|
|
{ MS_FILTER_GET_STAT_OUTPUT, winsndds_get_stat_ouptut },
|
|
{ MS_FILTER_GET_STAT_DISCARDED, winsndds_get_stat_discarded },
|
|
{ 0 , NULL }
|
|
};
|
|
|
|
MSFilterDesc winsndds_read_desc={
|
|
MS_WINSNDDS_READ_ID,
|
|
"MSWinSndRead",
|
|
"Sound capture filter for Windows Sound drivers",
|
|
MS_FILTER_OTHER,
|
|
NULL,
|
|
0,
|
|
1,
|
|
winsndds_init,
|
|
winsndds_read_preprocess,
|
|
winsndds_read_process,
|
|
winsndds_read_postprocess,
|
|
winsndds_uninit,
|
|
winsndds_methods
|
|
};
|
|
|
|
|
|
MSFilterDesc winsndds_write_desc={
|
|
MS_WINSNDDS_WRITE_ID,
|
|
"MSWinSndWrite",
|
|
"Sound playback filter for Windows Sound drivers",
|
|
MS_FILTER_OTHER,
|
|
NULL,
|
|
1,
|
|
0,
|
|
winsndds_init,
|
|
winsndds_write_preprocess,
|
|
winsndds_write_process,
|
|
winsndds_write_postprocess,
|
|
winsndds_uninit,
|
|
winsndds_methods
|
|
};
|
|
|
|
MSFilter *ms_winsndds_read_new(MSSndCard *card){
|
|
MSFilter *f=ms_filter_new_from_desc(&winsndds_read_desc);
|
|
WinSndDsCard *wc=(WinSndDsCard*)card->data;
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
d->dev_id=wc->in_devid;
|
|
memcpy(&d->in_guid, &wc->in_guid, sizeof(GUID));
|
|
memcpy(&d->out_guid, &wc->out_guid, sizeof(GUID));
|
|
return f;
|
|
}
|
|
|
|
|
|
MSFilter *ms_winsndds_write_new(MSSndCard *card){
|
|
MSFilter *f=ms_filter_new_from_desc(&winsndds_write_desc);
|
|
WinSndDsCard *wc=(WinSndDsCard*)card->data;
|
|
WinSndDs *d=(WinSndDs*)f->data;
|
|
d->dev_id=wc->out_devid;
|
|
memcpy(&d->in_guid, &wc->in_guid, sizeof(GUID));
|
|
memcpy(&d->out_guid, &wc->out_guid, sizeof(GUID));
|
|
return f;
|
|
}
|
|
|
|
MS_FILTER_DESC_EXPORT(winsndds_read_desc)
|
|
MS_FILTER_DESC_EXPORT(winsndds_write_desc)
|
|
|
|
#endif
|