2
mediastreamer2 library - modular sound and video processing and streaming
3
Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
5
This program is free software; you can redistribute it and/or
6
modify it under the terms of the GNU General Public License
7
as published by the Free Software Foundation; either version 2
8
of the License, or (at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
#if defined(_WIN32_WCE)
29
#include <speex/speex_preprocess.h>
32
#include "mediastreamer2/mssndcard.h"
33
#include "mediastreamer2/msfilter.h"
34
#include "mediastreamer2/msticker.h"
37
#include <malloc.h> /* for alloca */
46
MSFilter *ms_winsnd_read_new(MSSndCard *card);
47
MSFilter *ms_winsnd_write_new(MSSndCard *card);
49
typedef struct WinSndData{
55
WAVEHDR waveouthdr[30];
56
char waveoutbuffer[30][3200];
60
WAVEHDR waveinhdr[30];
62
char waveinbuffer[30][3200];
69
MSBufferizer * bufferizer;
75
SpeexPreprocessState *pst;
81
static uint64_t winsnd_get_cur_time( void *data){
82
WinSndData *d=(WinSndData*)data;
83
uint64_t curtime=(d->bytes_read*1000)/(d->rate*(d->bits/8)*((d->stereo==FALSE) ? 1 : 2));
84
ms_debug("winsnd_get_cur_time: bytes_read=%lu, rate=%i, bits=%i, stereo=%i return %lu\n",
85
(unsigned long)d->bytes_read,d->rate,d->bits,d->stereo,(unsigned long)curtime);
90
SpeakerCallback (HWAVEOUT _waveoutdev, UINT uMsg, DWORD dwInstance,
91
DWORD dwParam1, DWORD dwParam2)
99
ms_message("SpeakerCallback : WOM_OPEN");
102
ms_message("SpeakerCallback : WOM_CLOSE");
105
wHdr = (WAVEHDR *) dwParam1;
106
device = (WinSndData *)dwInstance;
107
device->buffer_playing--;
115
WaveInCallback (HWAVEIN waveindev, UINT uMsg, DWORD dwInstance, DWORD dwParam1,
119
MMRESULT mr = NOERROR;
122
device = (WinSndData *)dwInstance;
127
wHdr = (WAVEHDR *) dwParam1;
128
/* A waveform-audio data block has been played and
130
ms_message("WaveInCallback : MM_WOM_DONE");
131
waveInUnprepareHeader (waveindev, (LPWAVEHDR) wHdr, sizeof (WAVEHDR));
135
ms_message("WaveInCallback : WIM_OPEN");
138
ms_message("WaveInCallback : WIM_CLOSE");
141
wHdr = (WAVEHDR *) dwParam1;
143
if (!device->read_started && !device->write_started)
145
mr = waveInUnprepareHeader (device->waveindev, (LPWAVEHDR) wHdr, sizeof (WAVEHDR));
146
ms_warning("WaveInCallback : unprepare header (waveInUnprepareHeader:0x%i)", mr);
150
if (wHdr->dwBufferLength!=wHdr->dwBytesRecorded)
152
mr = waveInAddBuffer (device->waveindev,
154
sizeof (device->waveinhdr[wHdr->dwUser]));
155
if (mr != MMSYSERR_NOERROR)
157
ms_warning("WaveInCallback : error adding buffer to sound card (waveInAddBuffer:0x%i)", mr);
161
device->bytes_read+=wHdr->dwBytesRecorded;
162
ms_mutex_lock(&device->mutex);
163
if (device->read_started)
166
if (rm==NULL) rm=allocb(wHdr->dwBufferLength,0);
167
memcpy(rm->b_wptr,wHdr->lpData, wHdr->dwBufferLength);
169
#ifndef DISABLE_SPEEX
170
if (device->pst!=NULL)
173
//memset(rm->b_wptr,0, wHdr->dwBufferLength);
175
vad = speex_preprocess(device->pst, (short*)rm->b_wptr, NULL);
178
ms_message("WaveInCallback : %d", vad);
183
rm->b_wptr+=wHdr->dwBufferLength;
184
putq(&device->rq,rm);
187
ms_mutex_unlock(&device->mutex);
189
mr = waveInAddBuffer (device->waveindev,
191
sizeof (device->waveinhdr[wHdr->dwUser]));
192
if (mr != MMSYSERR_NOERROR)
194
ms_warning("WaveInCallback : error adding buffer to sound card (waveInAddBuffer:0x%i)", mr);
200
static int winsnd_open(WinSndData *device, int devnumber, int bits,int stereo, int rate, int *minsz)
202
MMRESULT mr = NOERROR;
208
device->wfx.wFormatTag = WAVE_FORMAT_PCM;
209
device->wfx.cbSize = 0;
210
device->wfx.nAvgBytesPerSec = 16000;
211
device->wfx.nBlockAlign = 2;
212
device->wfx.nChannels = channel;
213
device->wfx.nSamplesPerSec = rate; /* 8000; */
214
device->wfx.wBitsPerSample = bits;
216
dwFlag = CALLBACK_FUNCTION;
217
if (devnumber != WAVE_MAPPER)
218
dwFlag = WAVE_MAPPED | CALLBACK_FUNCTION;
219
mr = waveOutOpen (&(device->waveoutdev), devnumber, &(device->wfx), (DWORD) SpeakerCallback,
220
(DWORD)device, dwFlag);
223
ms_warning("Failed to open device: trying default device. (waveOutOpen:0x%i)", mr);
224
dwFlag = CALLBACK_FUNCTION;
225
mr = waveOutOpen (&(device->waveoutdev), WAVE_MAPPER, &(device->wfx), (DWORD) SpeakerCallback,
226
(DWORD)device, dwFlag);
230
ms_warning("Failed to open windows sound device. (waveOutOpen:0x%i)", mr);
234
/* prepare windows buffers */
237
for (i = 0; i < MAX_WAVEHDR; i++)
239
memset (&(device->waveouthdr[i]), 0, sizeof (device->waveouthdr[i]));
240
device->waveouthdr[i].lpData = device->waveoutbuffer[i];
241
/* BUG: on ne connait pas la taille des frames a recevoir...
242
on utilise enc_frame_per_packet au lien de dec_frame_per_packet */
244
device->waveouthdr[i].dwBufferLength = device->rate/8000 * 320;
245
/* 480 pour 98 (speex) */
246
device->waveouthdr[i].dwFlags = 0;
247
device->waveouthdr[i].dwUser = i;
249
mr = waveOutPrepareHeader (device->waveoutdev, &(device->waveouthdr[i]),
250
sizeof (device->waveouthdr[i]));
251
if (mr != MMSYSERR_NOERROR){
252
ms_warning("Failed to prepare windows sound device. (waveOutPrepareHeader:0x%i)", mr);
256
ms_message("Sound Header prepared %i for windows sound device. (waveOutPrepareHeader)", i);
261
/* Init Microphone device */
262
dwFlag = CALLBACK_FUNCTION;
263
if (devnumber != WAVE_MAPPER)
264
dwFlag = WAVE_MAPPED | CALLBACK_FUNCTION;
265
mr = waveInOpen (&(device->waveindev), devnumber, &(device->wfx),
266
(DWORD) WaveInCallback, (DWORD)device, dwFlag);
269
ms_warning("Failed to open device: trying default device. (waveInOpen:0x%i)", mr);
270
dwFlag = CALLBACK_FUNCTION;
271
mr = waveInOpen (&(device->waveindev), WAVE_MAPPER, &(device->wfx),
272
(DWORD) WaveInCallback, (DWORD)device, dwFlag);
277
ms_warning("Failed to prepare windows sound device. (waveInOpen:0x%i)", mr);
283
for (i = 0; i < MAX_WAVEHDR; i++)
285
memset (&(device->waveinhdr[i]), 0, sizeof (device->waveinhdr[i]));
286
device->waveinhdr[i].lpData = device->waveinbuffer[i];
288
device->waveinhdr[i].dwBufferLength = device->rate/8000 * 320;
289
device->waveinhdr[i].dwFlags = 0;
290
device->waveinhdr[i].dwUser = i;
291
mr = waveInPrepareHeader (device->waveindev, &(device->waveinhdr[i]),
292
sizeof (device->waveinhdr[i]));
293
if (mr == MMSYSERR_NOERROR){
294
mr = waveInAddBuffer (device->waveindev, &(device->waveinhdr[i]),
295
sizeof (device->waveinhdr[i]));
296
if (mr == MMSYSERR_NOERROR)
298
ms_message("Sound Header prepared %i for windows sound device. (waveInAddBuffer)", i);
302
ms_warning("Failed to prepare windows sound device. (waveInAddBuffer:0x%i)", mr);
307
ms_warning("Failed to prepare windows sound device. (waveInPrepareHeader:0x%i)", mr);
311
#ifndef DISABLE_SPEEX
313
device->pst = speex_preprocess_state_init((device->rate/8000 * 320)/2, device->rate);
314
if (device->pst!=NULL) {
317
speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_VAD, &i);
319
speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_DENOISE, &i);
321
speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_AGC, &i);
323
speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_AGC_LEVEL, &f);
325
speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_DEREVERB, &i);
327
speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);
329
speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);
333
device->bytes_read=0;
334
mr = waveInStart (device->waveindev);
335
if (mr != MMSYSERR_NOERROR)
337
ms_warning("Failed to start recording on windows sound device. (waveInStart:0x%i)", mr);
341
*minsz=device->rate/8000 * 320;
345
static void winsnd_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent)
347
WinSndData *d=(WinSndData*)card->data;
348
MMRESULT mr = NOERROR;
349
DWORD dwVolume = 0xFFFF;
350
dwVolume = ((0xFFFF) * percent) / 100;
352
if (d->mixdev==NULL) return;
354
case MS_SND_CARD_MASTER:
355
mr = waveOutSetVolume(d->waveoutdev, dwVolume);
356
if (mr != MMSYSERR_NOERROR)
358
ms_warning("Failed to set master volume. (waveOutSetVolume:0x%i)", mr);
364
case MS_SND_CARD_CAPTURE:
365
wincmd=SOUND_MIXER_IGAIN;
367
case MS_SND_CARD_PLAYBACK:
368
wincmd=SOUND_MIXER_PCM;
372
ms_warning("winsnd_card_set_level: unsupported command.");
377
static int winsnd_get_level(MSSndCard *card, MSSndCardMixerElem e)
379
WinSndData *d=(WinSndData*)card->data;
380
MMRESULT mr = NOERROR;
381
DWORD dwVolume = 0x0000;
383
if (d->mixdev==NULL) return -1;
385
case MS_SND_CARD_MASTER:
386
mr=waveOutGetVolume(d->waveoutdev, &dwVolume);
387
// Transform to 0 to 100 scale
388
//dwVolume = (dwVolume *100) / (0xFFFF);
392
case MS_SND_CARD_CAPTURE:
393
osscmd=SOUND_MIXER_IGAIN;
395
case MS_SND_CARD_PLAYBACK:
396
osscmd=SOUND_MIXER_PCM;
400
ms_warning("winsnd_card_get_level: unsupported command.");
406
static void winsnd_set_source(MSSndCard *card, MSSndCardCapture source)
408
WinSndData *d=(WinSndData*)card->data;
409
if (d->mixdev==NULL) return;
412
case MS_SND_CARD_MIC:
414
case MS_SND_CARD_LINE:
419
static void winsnd_init(MSSndCard *card){
420
WinSndData *d=ms_new(WinSndData,1);
421
memset(d, 0, sizeof(WinSndData));
424
d->sound_err=-1; /* not opened */
425
d->read_started=FALSE;
426
d->write_started=FALSE;
431
d->bufferizer=ms_bufferizer_new();
432
ms_mutex_init(&d->mutex,NULL);
434
#ifndef DISABLE_SPEEX
439
static void winsnd_uninit(MSSndCard *card){
440
WinSndData *d=(WinSndData*)card->data;
443
if (d->pcmdev!=NULL) ms_free(d->pcmdev);
444
if (d->mixdev!=NULL) ms_free(d->mixdev);
445
ms_bufferizer_destroy(d->bufferizer);
448
ms_mutex_destroy(&d->mutex);
450
#ifndef DISABLE_SPEEX
452
speex_preprocess_state_destroy(d->pst);
458
#define DSP_NAME "/dev/dsp"
459
#define MIXER_NAME "/dev/mixer"
461
static void winsnd_detect(MSSndCardManager *m);
462
static MSSndCard *winsnd_dup(MSSndCard *obj);
464
MSSndCardDesc winsnd_card_desc={
477
static MSSndCard *winsnd_dup(MSSndCard *obj){
478
MSSndCard *card=ms_snd_card_new(&winsnd_card_desc);
479
WinSndData *dcard=(WinSndData*)card->data;
480
WinSndData *dobj=(WinSndData*)obj->data;
481
dcard->pcmdev=ms_strdup(dobj->pcmdev);
482
dcard->mixdev=ms_strdup(dobj->mixdev);
483
card->name=ms_strdup(obj->name);
487
static MSSndCard *winsnd_card_new(const char *pcmdev, const char *mixdev){
488
MSSndCard *card=ms_snd_card_new(&winsnd_card_desc);
489
WinSndData *d=(WinSndData*)card->data;
490
d->pcmdev=ms_strdup(pcmdev);
491
d->mixdev=ms_strdup(mixdev);
492
card->name=ms_strdup(pcmdev);
496
static void winsnd_detect(MSSndCardManager *m){
497
MMRESULT mr = NOERROR;
498
unsigned int nInDevices = waveInGetNumDevs ();
499
/*unsigned int nOutDevices = waveOutGetNumDevs ();*/
504
for (item = 0; item < nInDevices; item++)
507
mr = waveInGetDevCaps (item, &caps, sizeof (WAVEINCAPS));
508
if (mr == MMSYSERR_NOERROR)
511
snprintf(pcmdev,sizeof(pcmdev),"%s",caps.szPname);
512
snprintf(mixdev,sizeof(mixdev),"%s",caps.szPname);
515
card=winsnd_card_new(pcmdev,mixdev);
516
ms_snd_card_manager_add_card(m,card);
518
card=winsnd_card_new(pcmdev,mixdev);
519
ms_snd_card_manager_add_card(m,card);
524
static void * winsnd_thread(void *p){
525
MSSndCard *card=(MSSndCard*)p;
526
WinSndData *d=(WinSndData*)card->data;
528
uint8_t *rtmpbuff=NULL;
529
uint8_t *wtmpbuff=NULL;
535
MMRESULT mr = NOERROR;
538
const MSList *elem = ms_snd_card_manager_get_list(ms_snd_card_manager_get());
540
/* look for devicenumber for card */
541
for (;elem!=NULL;elem=elem->next){
542
MSSndCard *acard=(MSSndCard*)elem->data;
550
d->sound_err=winsnd_open(d, devnumber, d->bits,d->stereo,d->rate,&bsize);
551
if (d->sound_err==0){
552
rtmpbuff=(uint8_t*)alloca(bsize);
553
wtmpbuff=(uint8_t*)alloca(bsize);
555
while(d->read_started || d->write_started){
556
if (d->sound_err==0){
558
if (d->read_started){
559
if (rm==NULL) rm=allocb(bsize,0);
561
/* get data from callback */
562
//err=read(d->pcmfd,rm->b_wptr,bsize);
566
ms_warning("Fail to read %i bytes from soundcard: %s",
567
bsize,strerror(errno));
575
//sz = read(d->pcmfd,rtmpbuff,bsize);
577
if( sz!=bsize) ms_warning("sound device read returned %i !",sz);
580
if (d->write_started){
582
if (d->buffer_playing<4 && d->buffer_playing<MAX_WAVEHDR)
584
/* remove extra buffer when latency is increasing:
585
this often happen with USB device */
586
ms_mutex_lock(&d->mutex);
587
if (d->bufferizer->size>=bsize*7){
588
ms_warning("Extra data for sound card removed (%ims)", 2*(bsize*20)/320);
589
err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
590
err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
592
err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
593
ms_mutex_unlock(&d->mutex);
596
/* write to sound devide! */
597
//err=write(d->pcmfd,wtmpbuff,bsize);
598
memcpy (d->waveouthdr[pos_whdr].lpData, wtmpbuff, bsize);
600
mr = waveOutWrite (d->waveoutdev,
601
&(d->waveouthdr[pos_whdr]),
602
sizeof (d->waveouthdr[pos_whdr]));
604
if (mr != MMSYSERR_NOERROR)
606
if (mr == WAVERR_STILLPLAYING)
609
/* data should go back to queue */
611
ms_warning("sound device write STILL_PLAYING (waveOutWrite:0x%i)", mr);
615
ms_warning("sound device write returned (waveOutWrite:0x%i)", mr);
622
if (pos_whdr == MAX_WAVEHDR)
623
pos_whdr = 0; /* loop over the prepared blocks */
628
#if !defined(_WIN32_WCE)
629
ms_warning("Fail to write %i bytes from soundcard: %s",
630
bsize,strerror(errno));
632
ms_warning("Fail to write %i bytes from soundcard: %i",
633
bsize,WSAGetLastError());
644
ms_mutex_lock(&d->mutex);
645
if (d->bufferizer->size>=bsize*4){
646
ms_warning("Extra data for sound card removed (%ims)", 3*(bsize*20)/320);
647
err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
648
err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
649
err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
651
ms_mutex_unlock(&d->mutex);
655
if (d->buffer_playing<3 && d->buffer_playing<MAX_WAVEHDR)
657
memset(wtmpbuff,0,bsize);
659
memcpy (d->waveouthdr[pos_whdr].lpData, wtmpbuff, bsize);
660
//sz = write(d->pcmfd,wtmpbuff,bsize);
661
mr = waveOutWrite (d->waveoutdev,
662
&(d->waveouthdr[pos_whdr]),
663
sizeof (d->waveouthdr[pos_whdr]));
665
if (mr != MMSYSERR_NOERROR)
667
if (mr == WAVERR_STILLPLAYING)
670
/* data should go back to queue */
672
ms_warning("sound device write STILL_PLAYING (waveOutWrite:0x%i)", mr);
676
ms_warning("sound device write returned (waveOutWrite:0x%i)", mr);
683
if (pos_whdr == MAX_WAVEHDR)
684
pos_whdr = 0; /* loop over the prepared blocks */
694
if (d->sound_err==0) {
697
/* close sound card */
699
/* unprepare buffer */
700
for (i = 0; i < MAX_WAVEHDR; i++)
703
for (counttry=0;counttry<10;counttry++)
705
mr = waveInUnprepareHeader (d->waveindev,
707
sizeof (d->waveinhdr[i]));
708
if (mr != MMSYSERR_NOERROR)
710
ms_error("Failed to unprepared %i buffer from sound card (waveInUnprepareHeader:0x%i", count, mr);
715
ms_message("successfully unprepared %i buffer from sound card.", count);
720
ms_warning("unprepared %i buffer from sound card.", count);
722
mr = waveInStop (d->waveindev);
723
if (mr != MMSYSERR_NOERROR)
725
ms_error("failed to stop recording sound card (waveInStop:0x%i)", mr);
728
ms_message("successfully stopped recording sound card");
731
mr = waveInReset (d->waveindev);
732
if (mr != MMSYSERR_NOERROR)
734
ms_warning("failed to reset recording sound card (waveInReset:0x%i)", mr);
737
ms_message("successful reset of recording sound card");
740
mr = waveInClose (d->waveindev);
741
if (mr != MMSYSERR_NOERROR)
743
ms_warning("failed to close recording sound card (waveInClose:0x%i)", mr);
746
ms_message("successfully closed recording sound card");
753
static void winsnd_start_r(MSSndCard *card){
754
WinSndData *d=(WinSndData*)card->data;
755
if (d->read_started==FALSE && d->write_started==FALSE){
756
d->read_started=TRUE;
757
ms_thread_create(&d->thread,NULL,winsnd_thread,card);
758
}else d->read_started=TRUE;
761
static void winsnd_stop_r(MSSndCard *card){
762
WinSndData *d=(WinSndData*)card->data;
763
d->read_started=FALSE;
764
if (d->write_started==FALSE){
765
ms_thread_join(d->thread,NULL);
769
static void winsnd_start_w(MSSndCard *card){
770
WinSndData *d=(WinSndData*)card->data;
771
if (d->read_started==FALSE && d->write_started==FALSE){
772
d->write_started=TRUE;
773
ms_thread_create(&d->thread,NULL,winsnd_thread,card);
775
d->write_started=TRUE;
779
static void winsnd_stop_w(MSSndCard *card){
780
WinSndData *d=(WinSndData*)card->data;
781
d->write_started=FALSE;
782
if (d->read_started==FALSE){
783
ms_thread_join(d->thread,NULL);
787
static mblk_t *winsnd_get(MSSndCard *card){
788
WinSndData *d=(WinSndData*)card->data;
790
ms_mutex_lock(&d->mutex);
792
ms_mutex_unlock(&d->mutex);
796
static void winsnd_put(MSSndCard *card, mblk_t *m){
797
WinSndData *d=(WinSndData*)card->data;
798
ms_mutex_lock(&d->mutex);
799
ms_bufferizer_put(d->bufferizer,m);
800
ms_mutex_unlock(&d->mutex);
804
static void winsnd_read_preprocess(MSFilter *f){
805
MSSndCard *card=(MSSndCard*)f->data;
806
winsnd_start_r(card);
807
ms_ticker_set_time_func(f->ticker,winsnd_get_cur_time,card->data);
810
static void winsnd_read_postprocess(MSFilter *f){
811
MSSndCard *card=(MSSndCard*)f->data;
812
ms_ticker_set_time_func(f->ticker,NULL,NULL);
816
static void winsnd_read_process(MSFilter *f){
817
MSSndCard *card=(MSSndCard*)f->data;
819
while((m=winsnd_get(card))!=NULL){
820
ms_queue_put(f->outputs[0],m);
824
static void winsnd_write_preprocess(MSFilter *f){
825
MSSndCard *card=(MSSndCard*)f->data;
826
winsnd_start_w(card);
829
static void winsnd_write_postprocess(MSFilter *f){
830
MSSndCard *card=(MSSndCard*)f->data;
834
static void winsnd_write_process(MSFilter *f){
835
MSSndCard *card=(MSSndCard*)f->data;
837
while((m=ms_queue_get(f->inputs[0]))!=NULL){
842
static int set_rate(MSFilter *f, void *arg){
843
MSSndCard *card=(MSSndCard*)f->data;
844
WinSndData *d=(WinSndData*)card->data;
845
d->rate=*((int*)arg);
849
static int set_nchannels(MSFilter *f, void *arg){
850
MSSndCard *card=(MSSndCard*)f->data;
851
WinSndData *d=(WinSndData*)card->data;
852
d->stereo=(*((int*)arg)==2);
857
static MSFilterMethod winsnd_methods[]={
858
{ MS_FILTER_SET_SAMPLE_RATE , set_rate },
859
{ MS_FILTER_SET_NCHANNELS , set_nchannels },
863
MSFilterDesc winsnd_read_desc={
866
"Sound capture filter for Windows Sound drivers",
872
winsnd_read_preprocess,
874
winsnd_read_postprocess,
880
MSFilterDesc winsnd_write_desc={
883
"Sound playback filter for Windows Sound drivers",
889
winsnd_write_preprocess,
890
winsnd_write_process,
891
winsnd_write_postprocess,
896
MSFilter *ms_winsnd_read_new(MSSndCard *card){
897
MSFilter *f=ms_filter_new_from_desc(&winsnd_read_desc);
903
MSFilter *ms_winsnd_write_new(MSSndCard *card){
904
MSFilter *f=ms_filter_new_from_desc(&winsnd_write_desc);
909
MS_FILTER_DESC_EXPORT(winsnd_read_desc)
910
MS_FILTER_DESC_EXPORT(winsnd_write_desc)