]> sjero.net Git - linphone/blob - linphone/mediastreamer2/src/winsnd2.c
56c8d404c900c6d0c5f2f8bc1d7cd34dca34f46e
[linphone] / linphone / mediastreamer2 / src / winsnd2.c
1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4
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.
9
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.
14
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.
18 */
19
20 #ifndef UNICODE
21 #define UNICODE
22 #endif
23
24 #include "mediastreamer2/mssndcard.h"
25 #include "mediastreamer2/msfilter.h"
26 #include "mediastreamer2/msticker.h"
27
28 #include <mmsystem.h>
29 #ifdef _MSC_VER
30 #include <mmreg.h>
31 #endif
32 #include <msacm.h>
33
34 #if defined(_WIN32_WCE)
35 /*#define DISABLE_SPEEX */
36 /*#define WCE_OPTICON_WORKAROUND 1000 */
37 #endif
38 #ifndef DISABLE_SPEEX
39 #include <speex/speex_preprocess.h>
40 #endif
41
42 #define WINSND_NBUFS 10
43 #define WINSND_OUT_NBUFS 20
44 #define WINSND_NSAMPLES 160
45 #define WINSND_MINIMUMBUFFER 5
46
47 static MSFilter *ms_winsnd_read_new(MSSndCard *card);
48 static MSFilter *ms_winsnd_write_new(MSSndCard *card);
49
50 typedef struct WinSndCard{
51         int in_devid;
52         int out_devid;
53         int removed;
54 }WinSndCard;
55
56 static void winsndcard_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent){
57         WinSndCard *d=(WinSndCard*)card->data;
58
59         UINT uMixerID;
60         DWORD dwMixerHandle;
61         MIXERLINE MixerLine;
62         MIXERLINE Line;
63         UINT uLineIndex;
64
65         MIXERLINECONTROLS mlc = {0};
66         MIXERCONTROL mc = {0};
67         MIXERCONTROLDETAILS mcd = {0};
68         MIXERCONTROLDETAILS_UNSIGNED mcdu = {0};
69
70         MMRESULT mr = MMSYSERR_NOERROR;
71         DWORD dwVolume = ((0xFFFF) * percent) / 100;
72         
73         WORD wLeftVol, wRightVol;
74         DWORD dwNewVol;
75         wLeftVol = LOWORD(dwVolume); // get higher WORD
76         wRightVol = LOWORD(dwVolume); // get lower WORD
77
78         dwNewVol = MAKELONG(wLeftVol, wRightVol);
79
80         switch(e){
81                 case MS_SND_CARD_PLAYBACK:
82                 case MS_SND_CARD_MASTER:
83                         {
84                                 mr = mixerGetID( (HMIXEROBJ)d->out_devid, &uMixerID, MIXER_OBJECTF_WAVEOUT );
85                                 if ( mr != MMSYSERR_NOERROR )
86                                 {
87                                         ms_error("winsndcard_set_level: mixerGetID failed. (0x%x)", mr);
88                                         return;
89                                 }
90                                 mr = mixerOpen( (LPHMIXER)&dwMixerHandle, uMixerID, 0L, 0L, 0L );
91                                 if ( mr != MMSYSERR_NOERROR )
92                                 {
93                                         mixerClose( (HMIXER)dwMixerHandle );
94                                         ms_error("winsndcard_set_level: mixerOpen failed. (0x%x)", mr);
95                                         return;
96                                 }
97                                 memset( &MixerLine, 0, sizeof(MIXERLINE) );
98                                 MixerLine.cbStruct = sizeof(MIXERLINE);
99                                 if (MS_SND_CARD_MASTER==e)
100                                         MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
101                                 else
102                                         MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
103                                 mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );
104                                 if ( mr != MMSYSERR_NOERROR )
105                                 {
106                                         mixerClose( (HMIXER)dwMixerHandle );
107                                         ms_error("winsndcard_set_level: mixerGetLineInfo failed. (0x%x)", mr);
108                                         return;
109                                 }
110
111                                 /* ms_message("Name: %s\n", MixerLine.szName); */
112                                 /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
113                                 /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
114
115                                 mlc.cbStruct = sizeof(MIXERLINECONTROLS);
116                                 mlc.dwLineID = MixerLine.dwLineID;
117                                 mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
118                                 mlc.cControls = 1;
119                                 mlc.pamxctrl = &mc;
120                                 mlc.cbmxctrl = sizeof(MIXERCONTROL);
121                                 mr = mixerGetLineControls((HMIXEROBJ)dwMixerHandle, 
122                                         &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
123
124
125                                 mcdu.dwValue = 65535*percent/100; /* the volume is a number between 0 and 65535 */
126
127                                 mcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
128                                 mcd.hwndOwner = 0;
129                                 mcd.dwControlID = mc.dwControlID;
130                                 mcd.paDetails = &mcdu;
131                                 mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
132                                 mcd.cChannels = 1;
133                                 mr = mixerSetControlDetails((HMIXEROBJ)dwMixerHandle, 
134                                         &mcd, MIXER_SETCONTROLDETAILSF_VALUE);
135
136                                 if (mr != MMSYSERR_NOERROR)
137                                 {
138                                         ms_error("winsndcard_set_level: mixerSetControlDetails failed. (0x%x)", mr);
139                                         return;
140                                 }
141                         }
142                         break;
143                 case MS_SND_CARD_CAPTURE:
144                         mr = mixerGetID( (HMIXEROBJ)d->in_devid, &uMixerID, MIXER_OBJECTF_WAVEIN );
145                         if ( mr != MMSYSERR_NOERROR )
146                         {
147                                 ms_error("winsndcard_set_level: mixerGetID failed. (0x%x)", mr);
148                                 return;
149                         }
150                         mr = mixerOpen( (LPHMIXER)&dwMixerHandle, uMixerID, 0L, 0L, 0L );
151                         if ( mr != MMSYSERR_NOERROR )
152                         {
153                                 mixerClose( (HMIXER)dwMixerHandle );
154                                 ms_error("winsndcard_set_level: mixerGetLineInfo failed. (0x%x)", mr);
155                                 return;
156                         }
157                         memset( &MixerLine, 0, sizeof(MIXERLINE) );
158                         MixerLine.cbStruct = sizeof(MIXERLINE);
159                         MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
160                         mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );
161                         if ( mr != MMSYSERR_NOERROR )
162                         {
163                                 mixerClose( (HMIXER)dwMixerHandle );
164                                 ms_error("winsndcard_set_level: mixerGetLineInfo failed. (0x%x)", mr);
165                                 return;
166                         }
167                         /* ms_message("Name: %s\n", MixerLine.szName); */
168                         /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
169                         /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
170
171                         mlc.cbStruct = sizeof(MIXERLINECONTROLS);
172                         mlc.dwLineID = MixerLine.dwLineID;
173                         mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
174                         mlc.cControls = 1;
175                         mlc.pamxctrl = &mc;
176                         mlc.cbmxctrl = sizeof(MIXERCONTROL);
177                         mr = mixerGetLineControls((HMIXEROBJ)dwMixerHandle, 
178                                 &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
179
180                         if (mr == MMSYSERR_NOERROR)
181                         {
182                                 mcdu.dwValue = 65535*percent/100; /* the volume is a number between 0 and 65535 */
183
184                                 mcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
185                                 mcd.hwndOwner = 0;
186                                 mcd.dwControlID = mc.dwControlID;
187                                 mcd.paDetails = &mcdu;
188                                 mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
189                                 mcd.cChannels = 1;
190                                 mr = mixerSetControlDetails((HMIXEROBJ)dwMixerHandle, 
191                                         &mcd, MIXER_SETCONTROLDETAILSF_VALUE);
192
193                                 if (mr == MMSYSERR_NOERROR)
194                                 {
195                                         return;
196                                 }
197                                 ms_error("winsndcard_set_level: mixerSetControlDetails failed. (0x%x)", mr);
198                                 ms_warning("winsndcard_set_level: control the SRC_MICROPHONE instead");
199                         }
200                         else
201                         {
202                                 ms_error("winsndcard_set_level: mixerGetLineControls failed. (0x%x)", mr);
203                                 ms_warning("winsndcard_set_level: control the SRC_MICROPHONE instead");
204                         }
205
206                         /* In case capture doesn't work: use the SRC_MICROPHONE volume */
207
208                         for (uLineIndex = 0; uLineIndex < MixerLine.cConnections; uLineIndex++)
209                         {
210                                 memset( &Line, 0, sizeof(MIXERLINE) );
211                                 Line.cbStruct = sizeof(MIXERLINE);
212                                 Line.dwDestination = MixerLine.dwDestination;
213                                 Line.dwSource = uLineIndex;
214                                 mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &Line, MIXER_GETLINEINFOF_LINEID);
215                                 if ( mr != MMSYSERR_NOERROR )
216                                 {
217                                         mixerClose( (HMIXER)dwMixerHandle );
218                                         ms_error("winsndcard_set_level: mixerGetLineInfo failed. (0x%x)", mr);
219                                         return;
220                                 }
221
222                                 /* ms_message("Name: %s\n", MixerLine.szName); */
223                                 /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
224                                 /* ms_message("LineID: %d\n", MixerLine.dwLineID); */
225                                 /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
226
227                                 memset( &Line, 0, sizeof(MIXERLINE) );
228                                 Line.cbStruct = sizeof(MIXERLINE);
229                                 Line.dwDestination = MixerLine.dwDestination;
230                                 Line.dwSource = uLineIndex;
231                                 mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &Line, MIXER_GETLINEINFOF_SOURCE);
232                                 if ( mr != MMSYSERR_NOERROR )
233                                 {
234                                         mixerClose( (HMIXER)dwMixerHandle );
235                                         ms_error("winsndcard_set_level: mixerGetLineInfo failed. (0x%x)", mr);
236                                         return;
237                                 }
238
239                                 /* ms_message("Name: %s\n", MixerLine.szName); */
240                                 /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
241                                 /* ms_message("LineID: %d\n", MixerLine.dwLineID); */
242                                 /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
243
244                                 if (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == Line.dwComponentType)  
245                                 {
246                                         LPMIXERCONTROL pmxctrl = (LPMIXERCONTROL)malloc(sizeof(MIXERCONTROL));
247                                         MIXERLINECONTROLS mxlctrl = {sizeof(mxlctrl), Line.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME, 1, sizeof(MIXERCONTROL), pmxctrl};  
248                                         if(!mixerGetLineControls((HMIXEROBJ)dwMixerHandle, &mxlctrl,  
249                                                 MIXER_GETLINECONTROLSF_ONEBYTYPE)){  
250                                                         DWORD cChannels = Line.cChannels;
251                                                         LPMIXERCONTROLDETAILS_UNSIGNED pUnsigned;
252                                                         MIXERCONTROLDETAILS mxcd;
253                                                         if (MIXERCONTROL_CONTROLF_UNIFORM & pmxctrl->fdwControl)  
254                                                                 cChannels = 1;  
255                                                         pUnsigned =  
256                                                                 (LPMIXERCONTROLDETAILS_UNSIGNED)  
257                                                                 malloc(cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
258
259                                                         mxcd.cbStruct = sizeof(mxcd);
260                                                         mxcd.dwControlID = pmxctrl->dwControlID;
261                                                         mxcd.cChannels = cChannels;
262                                                         mxcd.hwndOwner = (HWND)0;
263                                                         mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
264                                                         mxcd.paDetails = (LPVOID) pUnsigned;
265
266                                                         mixerGetControlDetails((HMIXEROBJ)dwMixerHandle, &mxcd,  
267                                                                 MIXER_SETCONTROLDETAILSF_VALUE);  
268                                                         pUnsigned[0].dwValue = pUnsigned[cChannels - 1].dwValue
269                                                                 =  pmxctrl->Bounds.dwMaximum*percent/100;
270                                                         mixerSetControlDetails((HMIXEROBJ)dwMixerHandle, &mxcd,  
271                                                                 MIXER_SETCONTROLDETAILSF_VALUE);  
272                                                         free(pmxctrl);  
273                                                         free(pUnsigned);  
274                                         }  
275                                         else  
276                                                 free(pmxctrl);  
277                                 }
278                         }
279                         mixerClose( (HMIXER)dwMixerHandle );
280                         if (mr != MMSYSERR_NOERROR)
281                         {
282                                 ms_error("winsndcard_set_level: mixerClose failed. (0x%x)", mr);
283                                 return;
284                         }
285                         break;
286                 default:
287                         ms_warning("winsnd_card_set_level: unsupported command.");
288         }
289 }
290
291 static int winsndcard_get_level(MSSndCard *card, MSSndCardMixerElem e){
292         WinSndCard *d=(WinSndCard*)card->data;
293
294         UINT uMixerID;
295         DWORD dwMixerHandle;
296         MIXERLINE MixerLine;
297         MIXERLINE Line;
298         UINT uLineIndex;
299
300         MIXERLINECONTROLS mlc = {0};
301         MIXERCONTROL mc = {0};
302         MIXERCONTROLDETAILS mcd = {0};
303         MIXERCONTROLDETAILS_UNSIGNED mcdu = {0};
304
305         MMRESULT mr = MMSYSERR_NOERROR;
306         int percent;
307
308         switch(e){
309                 case MS_SND_CARD_MASTER:
310                 case MS_SND_CARD_PLAYBACK:
311                         {
312                                 mr = mixerGetID( (HMIXEROBJ)d->out_devid, &uMixerID, MIXER_OBJECTF_WAVEOUT );
313                                 if ( mr != MMSYSERR_NOERROR )
314                                 {
315                                         ms_error("winsndcard_get_level: mixerGetID failed. (0x%x)", mr);
316                                         return -1;
317                                 }
318                                 mr = mixerOpen( (LPHMIXER)&dwMixerHandle, uMixerID, 0L, 0L, 0L );
319                                 if ( mr != MMSYSERR_NOERROR )
320                                 {
321                                         mixerClose( (HMIXER)dwMixerHandle );
322                                         ms_error("winsndcard_get_level: mixerOpen failed. (0x%x)", mr);
323                                         return -1;
324                                 }
325                                 memset( &MixerLine, 0, sizeof(MIXERLINE) );
326                                 MixerLine.cbStruct = sizeof(MIXERLINE);
327                                 if (MS_SND_CARD_MASTER==e)
328                                         MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
329                                 else
330                                         MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
331                                 mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );
332                                 if ( mr != MMSYSERR_NOERROR )
333                                 {
334                                         mixerClose( (HMIXER)dwMixerHandle );
335                                         ms_error("winsndcard_get_level: mixerGetLineInfo failed. (0x%x)", mr);
336                                         return -1;
337                                 }
338
339                                 /* ms_message("Name: %s\n", MixerLine.szName); */
340                                 /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
341                                 /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
342
343                                 mlc.cbStruct = sizeof(MIXERLINECONTROLS);
344                                 mlc.dwLineID = MixerLine.dwLineID;
345                                 mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
346                                 mlc.cControls = 1;
347                                 mlc.pamxctrl = &mc;
348                                 mlc.cbmxctrl = sizeof(MIXERCONTROL);
349                                 mr = mixerGetLineControls((HMIXEROBJ)dwMixerHandle, 
350                                         &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
351                                 if (mr != MMSYSERR_NOERROR)
352                                 {
353                                         ms_error("winsndcard_get_level: mixerGetLineControls failed. (0x%x)", mr);
354                                         return -1;
355                                 }
356
357                                 mcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
358                                 mcd.hwndOwner = 0;
359                                 mcd.dwControlID = mc.dwControlID;
360                                 mcd.paDetails = &mcdu;
361                                 mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
362                                 mcd.cChannels = 1;
363                                 mr = mixerGetControlDetails((HMIXEROBJ)dwMixerHandle, &mcd,  
364                                         MIXER_SETCONTROLDETAILSF_VALUE);  
365                                 percent = (mcdu.dwValue *100) / (mc.Bounds.dwMaximum);
366
367                                 if (mr != MMSYSERR_NOERROR)
368                                 {
369                                         ms_error("winsndcard_get_level: mixerGetControlDetails failed. (0x%x)", mr);
370                                         return -1;
371                                 }
372                                 return percent;
373                         }
374                         break;
375                 case MS_SND_CARD_CAPTURE:
376                         mr = mixerGetID( (HMIXEROBJ)d->in_devid, &uMixerID, MIXER_OBJECTF_WAVEIN );
377                         if ( mr != MMSYSERR_NOERROR )
378                         {
379                                 ms_error("winsndcard_get_level: mixerGetID failed. (0x%x)", mr);
380                                 return -1;
381                         }
382                         mr = mixerOpen( (LPHMIXER)&dwMixerHandle, uMixerID, 0L, 0L, 0L );
383                         if ( mr != MMSYSERR_NOERROR )
384                         {
385                                 mixerClose( (HMIXER)dwMixerHandle );
386                                 ms_error("winsndcard_get_level: mixerOpen failed. (0x%x)", mr);
387                                 return -1;
388                         }
389                         memset( &MixerLine, 0, sizeof(MIXERLINE) );
390                         MixerLine.cbStruct = sizeof(MIXERLINE);
391                         MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
392                         mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );
393                         if ( mr != MMSYSERR_NOERROR )
394                         {
395                                 mixerClose( (HMIXER)dwMixerHandle );
396                                 ms_error("winsndcard_get_level: mixerGetLineInfo failed. (0x%x)", mr);
397                                 return -1;
398                         }
399
400                         mlc.cbStruct = sizeof(MIXERLINECONTROLS);
401                         mlc.dwLineID = MixerLine.dwLineID;
402                         mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
403                         mlc.cControls = 1;
404                         mlc.pamxctrl = &mc;
405                         mlc.cbmxctrl = sizeof(MIXERCONTROL);
406                         mr = mixerGetLineControls((HMIXEROBJ)dwMixerHandle, 
407                                 &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
408                         if (mr == MMSYSERR_NOERROR)
409                         {
410                                 mcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
411                                 mcd.hwndOwner = 0;
412                                 mcd.dwControlID = mc.dwControlID;
413                                 mcd.paDetails = &mcdu;
414                                 mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
415                                 mcd.cChannels = 1;
416                                 mr = mixerGetControlDetails((HMIXEROBJ)dwMixerHandle, &mcd,  
417                                         MIXER_SETCONTROLDETAILSF_VALUE);  
418                                 percent = (mcdu.dwValue *100) / (mc.Bounds.dwMaximum);
419
420                                 if (mr == MMSYSERR_NOERROR)
421                                 {
422                                         return percent;
423                                 }
424                                 ms_error("winsndcard_get_level: mixerGetControlDetails failed. (0x%x)", mr);
425                                 ms_warning("winsndcard_get_level: control the SRC_MICROPHONE instead");
426                         }
427                         else
428                         {
429                                 ms_error("winsndcard_get_level: mixerGetLineControls failed. (0x%x)", mr);
430                                 ms_warning("winsndcard_get_level: control the SRC_MICROPHONE instead");
431                         }
432         
433                         /* ms_message("Name: %s\n", MixerLine.szName); */
434                         /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
435                         /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
436
437                         for (uLineIndex = 0; uLineIndex < MixerLine.cConnections; uLineIndex++)
438                         {
439                                 memset( &Line, 0, sizeof(MIXERLINE) );
440                                 Line.cbStruct = sizeof(MIXERLINE);
441                                 Line.dwDestination = MixerLine.dwDestination;
442                                 Line.dwSource = uLineIndex;
443                                 mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &Line, MIXER_GETLINEINFOF_LINEID);
444                                 if ( mr != MMSYSERR_NOERROR )
445                                 {
446                                         mixerClose( (HMIXER)dwMixerHandle );
447                                         ms_error("winsndcard_get_level: mixerGetLineInfo failed. (0x%x)", mr);
448                                         return -1;
449                                 }
450
451                                 /* ms_message("Name: %s\n", MixerLine.szName); */
452                                 /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
453                                 /* ms_message("LineID: %d\n", MixerLine.dwLineID); */
454                                 /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
455
456                                 memset( &Line, 0, sizeof(MIXERLINE) );
457                                 Line.cbStruct = sizeof(MIXERLINE);
458                                 Line.dwDestination = MixerLine.dwDestination;
459                                 Line.dwSource = uLineIndex;
460                                 mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &Line, MIXER_GETLINEINFOF_SOURCE);
461                                 if ( mr != MMSYSERR_NOERROR )
462                                 {
463                                         mixerClose( (HMIXER)dwMixerHandle );
464                                         ms_error("winsndcard_get_level: mixerGetLineInfo failed. (0x%x)", mr);
465                                         return -1;
466                                 }
467
468                                 /* ms_message("Name: %s\n", MixerLine.szName); */
469                                 /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
470                                 /* ms_message("LineID: %d\n", MixerLine.dwLineID); */
471                                 /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
472
473                                 if (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == Line.dwComponentType)  
474                                 {
475                                         LPMIXERCONTROL pmxctrl = (LPMIXERCONTROL)malloc(sizeof(MIXERCONTROL));
476                                         MIXERLINECONTROLS mxlctrl = {sizeof(mxlctrl), Line.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME, 1, sizeof(MIXERCONTROL), pmxctrl};  
477                                         if(!mixerGetLineControls((HMIXEROBJ)dwMixerHandle, &mxlctrl,  
478                                                 MIXER_GETLINECONTROLSF_ONEBYTYPE)){  
479                                                         DWORD cChannels = Line.cChannels;
480                                                         LPMIXERCONTROLDETAILS_UNSIGNED pUnsigned;
481                                                         MIXERCONTROLDETAILS mxcd;
482                                                         if (MIXERCONTROL_CONTROLF_UNIFORM & pmxctrl->fdwControl)  
483                                                                 cChannels = 1;  
484                                                         pUnsigned =  
485                                                                 (LPMIXERCONTROLDETAILS_UNSIGNED)  
486                                                                 malloc(cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
487
488                                                         mxcd.cbStruct = sizeof(mxcd);
489                                                         mxcd.dwControlID = pmxctrl->dwControlID;
490                                                         mxcd.cChannels = cChannels;
491                                                         mxcd.hwndOwner = (HWND)0;
492                                                         mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
493                                                         mxcd.paDetails = (LPVOID) pUnsigned;
494
495                                                         mixerGetControlDetails((HMIXEROBJ)dwMixerHandle, &mxcd,  
496                                                                 MIXER_SETCONTROLDETAILSF_VALUE);  
497                                                         percent = (pUnsigned[0].dwValue *100) / (pmxctrl->Bounds.dwMaximum);
498                                                         free(pmxctrl);
499                                                         free(pUnsigned);
500                                         }  
501                                         else  
502                                                 free(pmxctrl);  
503                                 }
504                         }
505                         mixerClose( (HMIXER)dwMixerHandle );
506                         if (mr != MMSYSERR_NOERROR)
507                         {
508                                 ms_error("winsndcard_get_level: mixerClose failed. (0x%x)", mr);
509                                 return -1;
510                         }
511                         return percent;
512                         break;
513                 default:
514                         ms_warning("winsndcard_get_level: unsupported command.");
515                         return -1;
516         }
517         return -1;
518 }
519
520 static void winsndcard_set_source(MSSndCard *card, MSSndCardCapture source){
521
522         switch(source){
523                 case MS_SND_CARD_MIC:
524                         break;
525                 case MS_SND_CARD_LINE:
526                         break;
527         }       
528 }
529
530 static int winsndcard_set_control(MSSndCard *card, MSSndCardControlElem e, int val){
531         WinSndCard *d=(WinSndCard*)card->data;
532
533         UINT uMixerID;
534         DWORD dwMixerHandle;
535         MIXERLINE MixerLine;
536         MIXERLINE Line;
537         UINT uLineIndex;
538
539         MIXERLINECONTROLS mlc = {0};
540         MIXERCONTROL mc = {0};
541         MIXERCONTROLDETAILS mcd = {0};
542         MIXERCONTROLDETAILS_BOOLEAN bMute;
543
544         MMRESULT mr = MMSYSERR_NOERROR;
545
546         switch(e){
547                 case MS_SND_CARD_CAPTURE_MUTE:
548
549                         bMute.fValue = (val>0);
550
551                         mr = mixerGetID( (HMIXEROBJ)d->in_devid, &uMixerID, MIXER_OBJECTF_WAVEIN );
552                         if ( mr != MMSYSERR_NOERROR )
553                         {
554                                 ms_error("winsndcard_set_control: mixerGetID failed. (0x%x)", mr);
555                                 return -1;
556                         }
557                         mr = mixerOpen( (LPHMIXER)&dwMixerHandle, uMixerID, 0L, 0L, 0L );
558                         if ( mr != MMSYSERR_NOERROR )
559                         {
560                                 mixerClose( (HMIXER)dwMixerHandle );
561                                 ms_error("winsndcard_set_control: mixerOpen failed. (0x%x)", mr);
562                                 return -1;
563                         }
564                         memset( &MixerLine, 0, sizeof(MIXERLINE) );
565                         MixerLine.cbStruct = sizeof(MIXERLINE);
566                         MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
567                         mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );
568                         if ( mr != MMSYSERR_NOERROR )
569                         {
570                                 mixerClose( (HMIXER)dwMixerHandle );
571                                 ms_error("winsndcard_set_control: mixerGetLineInfo failed. (0x%x)", mr);
572                                 return -1;
573                         }
574                         /* ms_message("Name: %s\n", MixerLine.szName); */
575                         /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
576                         /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
577
578                         for (uLineIndex = 0; uLineIndex < MixerLine.cConnections; uLineIndex++)
579                         {
580                                 memset( &Line, 0, sizeof(MIXERLINE) );
581                                 Line.cbStruct = sizeof(MIXERLINE);
582                                 Line.dwDestination = MixerLine.dwDestination;
583                                 Line.dwSource = uLineIndex;
584                                 mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &Line, MIXER_GETLINEINFOF_LINEID);
585                                 if ( mr != MMSYSERR_NOERROR )
586                                 {
587                                         mixerClose( (HMIXER)dwMixerHandle );
588                                         ms_error("winsndcard_set_control: mixerGetLineInfo failed. (0x%x)", mr);
589                                         return -1;
590                                 }
591                                 
592                                 /* ms_message("Name: %s\n", MixerLine.szName); */
593                                 /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
594                                 /* ms_message("LineID: %d\n", MixerLine.dwLineID); */
595                                 /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
596
597                                 memset( &Line, 0, sizeof(MIXERLINE) );
598                                 Line.cbStruct = sizeof(MIXERLINE);
599                                 Line.dwDestination = MixerLine.dwDestination;
600                                 Line.dwSource = uLineIndex;
601                                 mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &Line, MIXER_GETLINEINFOF_SOURCE);
602                                 if ( mr != MMSYSERR_NOERROR )
603                                 {
604                                         mixerClose( (HMIXER)dwMixerHandle );
605                                         ms_error("winsndcard_set_control: mixerGetLineInfo failed. (0x%x)", mr);
606                                         return -1;
607                                 }
608
609                                 /* ms_message("Name: %s\n", MixerLine.szName); */
610                                 /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
611                                 /* ms_message("LineID: %d\n", MixerLine.dwLineID); */
612                                 /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
613
614                                 if (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == Line.dwComponentType)  
615                                 {
616                                         /* unmute */
617                                         /* Find a mute control, if any, of the microphone line  */
618
619                                         LPMIXERCONTROL pmxctrl = (LPMIXERCONTROL)malloc(sizeof(MIXERCONTROL));
620                                         MIXERLINECONTROLS mxlctrl = {sizeof(mxlctrl), Line.dwLineID, MIXERCONTROL_CONTROLTYPE_MUTE, 1, sizeof(MIXERCONTROL), pmxctrl};  
621                                         if(!mixerGetLineControls((HMIXEROBJ)dwMixerHandle, &mxlctrl, MIXER_GETLINECONTROLSF_ONEBYTYPE)){  
622                                                 DWORD cChannels = Line.cChannels;
623                                                 LPMIXERCONTROLDETAILS_BOOLEAN pbool;
624                                                 MIXERCONTROLDETAILS mxcd;
625
626                                                 if (MIXERCONTROL_CONTROLF_UNIFORM & pmxctrl->fdwControl)  
627                                                         cChannels = 1;  
628                                                 pbool = (LPMIXERCONTROLDETAILS_BOOLEAN) malloc(cChannels * sizeof(
629                                                         MIXERCONTROLDETAILS_BOOLEAN));
630
631                                                 mxcd.cbStruct = sizeof(mxcd);
632                                                 mxcd.dwControlID = pmxctrl->dwControlID;
633                                                 mxcd.cChannels = cChannels;
634                                                 mxcd.hwndOwner = (HWND)0;
635                                                 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
636                                                 mxcd.paDetails = (LPVOID) pbool;
637
638                                                 mixerGetControlDetails((HMIXEROBJ)dwMixerHandle, &mxcd,  
639                                                         MIXER_SETCONTROLDETAILSF_VALUE);  
640                                                 /* Unmute the microphone line (for both channels) */
641                                                 pbool[0].fValue = pbool[cChannels - 1].fValue = val; /* 0 -> unmute; */
642                                                 mixerSetControlDetails((HMIXEROBJ)dwMixerHandle, &mxcd,  
643                                                         MIXER_SETCONTROLDETAILSF_VALUE);  
644                                                 free(pmxctrl);  
645                                                 free(pbool);  
646                                         }  
647                                         else  
648                                                 free(pmxctrl);  
649                                 }
650                         }
651                         mixerClose( (HMIXER)dwMixerHandle );
652                         if (mr != MMSYSERR_NOERROR)
653                         {
654                                 ms_error("winsndcard_set_control: mixerClose failed. (0x%x)", mr);
655                                 return -1;
656                         }
657                         return 0;
658
659                 case MS_SND_CARD_MASTER_MUTE:
660                 case MS_SND_CARD_PLAYBACK_MUTE:
661                         {
662                                 bMute.fValue = (val>0);
663
664                                 mr = mixerGetID( (HMIXEROBJ)d->out_devid, &uMixerID, MIXER_OBJECTF_WAVEOUT );
665                                 if ( mr != MMSYSERR_NOERROR )
666                                 {
667                                         ms_error("winsndcard_set_control: mixerGetID failed. (0x%x)", mr);
668                                         return -1;
669                                 }
670                                 mr = mixerOpen( (LPHMIXER)&dwMixerHandle, uMixerID, 0L, 0L, 0L );
671                                 if ( mr != MMSYSERR_NOERROR )
672                                 {
673                                         mixerClose( (HMIXER)dwMixerHandle );
674                                         ms_error("winsndcard_set_control: mixerOpen failed. (0x%x)", mr);
675                                         return -1;
676                                 }
677                                 memset( &MixerLine, 0, sizeof(MIXERLINE) );
678                                 MixerLine.cbStruct = sizeof(MIXERLINE);
679                                 if (MS_SND_CARD_MASTER_MUTE==e)
680                                         MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
681                                 else
682                                         MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
683                                 mr = mixerGetLineInfo( (HMIXEROBJ)dwMixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );
684                                 if ( mr != MMSYSERR_NOERROR )
685                                 {
686                                         mixerClose( (HMIXER)dwMixerHandle );
687                                         ms_error("winsndcard_set_control: mixerSetControlDetails failed. (0x%x)", mr);
688                                         return -1;
689                                 }
690
691                                 /* ms_message("Name: %s\n", MixerLine.szName); */
692                                 /* ms_message("Source Line: %d\n", MixerLine.dwSource); */
693                                 /* ms_message("ComponentType: %d\n", MixerLine.dwComponentType); */
694
695                                 mlc.cbStruct = sizeof(MIXERLINECONTROLS);
696                                 mlc.dwLineID = MixerLine.dwLineID;
697                                 mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE; //MIXERCONTROL_CONTROLTYPE_VOLUME;
698                                 mlc.cControls = 1;
699                                 mlc.pamxctrl = &mc;
700                                 mlc.cbmxctrl = sizeof(MIXERCONTROL);
701                                 mr = mixerGetLineControls((HMIXEROBJ)dwMixerHandle, 
702                                         &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
703
704                                 mcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
705                                 mcd.hwndOwner = 0;
706                                 mcd.dwControlID = mc.dwControlID;
707                                 mcd.paDetails = &bMute;
708                                 mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
709                                 mcd.cChannels = 1;
710                                 mr = mixerSetControlDetails((HMIXEROBJ)dwMixerHandle, 
711                                         &mcd, MIXER_SETCONTROLDETAILSF_VALUE);
712
713                                 if (mr != MMSYSERR_NOERROR)
714                                 {
715                                         ms_error("winsndcard_set_control: mixerSetControlDetails failed. (0x%x)", mr);
716                                         return -1;
717                                 }
718                                 return 0;
719                         }
720                         break;
721                 default:
722                         ms_warning("winsndcard_set_control: unsupported command.");
723         }
724         return -1;
725 }
726
727 static int winsndcard_get_control(MSSndCard *card, MSSndCardControlElem e){
728         WinSndCard *d=(WinSndCard*)card->data;
729         return -1;
730 }
731
732 static void winsndcard_init(MSSndCard *card){
733         WinSndCard *c=(WinSndCard *)ms_new(WinSndCard,1);
734         c->removed=0;
735         card->data=c;
736 }
737
738 static void winsndcard_uninit(MSSndCard *card){
739         ms_free(card->data);
740 }
741
742 static void winsndcard_detect(MSSndCardManager *m);
743 static  MSSndCard *winsndcard_dup(MSSndCard *obj);
744 static void winsndcard_unload(MSSndCardManager *m);
745
746 MSSndCardDesc winsnd_card_desc={
747         "MME",
748         winsndcard_detect,
749         winsndcard_init,
750         winsndcard_set_level,
751         winsndcard_get_level,
752         winsndcard_set_source,
753         winsndcard_set_control,
754         winsndcard_get_control,
755         ms_winsnd_read_new,
756         ms_winsnd_write_new,
757         winsndcard_uninit,
758         winsndcard_dup,
759         winsndcard_unload
760 };
761
762 static  MSSndCard *winsndcard_dup(MSSndCard *obj){
763         MSSndCard *card=ms_snd_card_new(&winsnd_card_desc);
764         card->name=ms_strdup(obj->name);
765         card->data=ms_new(WinSndCard,1);
766         memcpy(card->data,obj->data,sizeof(WinSndCard));
767         return card;
768 }
769
770 static MSSndCard *winsndcard_new(const char *name, int in_dev, int out_dev, unsigned cap){
771         MSSndCard *card=ms_snd_card_new(&winsnd_card_desc);
772         WinSndCard *d=(WinSndCard*)card->data;
773         card->name=ms_strdup(name);
774         d->in_devid=in_dev;
775         d->out_devid=out_dev;
776         card->capabilities=cap;
777         return card;
778 }
779
780 static void add_or_update_card(MSSndCardManager *m, const char *name, int indev, int outdev, unsigned int capability){
781         MSSndCard *card;
782         const MSList *elem=ms_snd_card_manager_get_list(m);
783         for(;elem!=NULL;elem=elem->next){
784                 card=(MSSndCard*)elem->data;
785                 if (strcmp(card->desc->driver_type, winsnd_card_desc.driver_type)==0
786                         && strcmp(card->name,name)==0){
787                         /*update already entered card */
788                         WinSndCard *d=(WinSndCard*)card->data;
789                         card->capabilities|=capability;
790                         if (indev!=-1) 
791                                 d->in_devid=indev;
792                         if (outdev!=-1)
793                                 d->out_devid=outdev;
794                         d->removed=0;
795                         return;
796                 }
797         }
798         /* add this new card:*/
799         ms_snd_card_manager_add_card(m,winsndcard_new(name,indev,outdev,capability));
800 }
801
802
803 static void _winsndcard_detect(MSSndCardManager *m){
804         MMRESULT mr = NOERROR;
805         unsigned int nOutDevices = waveOutGetNumDevs ();
806         unsigned int nInDevices = waveInGetNumDevs ();
807         unsigned int item;
808
809         if (nOutDevices>nInDevices)
810                 nInDevices = nOutDevices;
811
812         for (item = 0; item < nInDevices; item++){
813
814                 WAVEINCAPS incaps;
815                 WAVEOUTCAPS outcaps;
816                 mr = waveInGetDevCaps (item, &incaps, sizeof (WAVEINCAPS));
817                 if (mr == MMSYSERR_NOERROR)
818                 {
819 #if defined(_WIN32_WCE)
820                         char card[256];
821                         snprintf(card, sizeof(card), "Input card %i", item);
822                         add_or_update_card(m,card,item,-1,MS_SND_CARD_CAP_CAPTURE);
823                         /* _tprintf(L"new card: %s", incaps.szPname); */
824 #else
825                         char szName[256];
826                         WideCharToMultiByte(CP_UTF8,0,incaps.szPname,-1,szName,256,0,0);
827                         add_or_update_card(m,szName,item,-1,MS_SND_CARD_CAP_CAPTURE);
828 #endif
829                 }
830                 mr = waveOutGetDevCaps (item, &outcaps, sizeof (WAVEOUTCAPS));
831                 if (mr == MMSYSERR_NOERROR)
832                 {
833 #if defined(_WIN32_WCE)
834                         char card[256];
835                         snprintf(card, sizeof(card), "Output card %i", item);
836                         add_or_update_card(m,card,-1,item,MS_SND_CARD_CAP_PLAYBACK);
837                         /* _tprintf(L"new card: %s", outcaps.szPname); */
838 #else
839                         char szName[256];
840                         WideCharToMultiByte(CP_UTF8,0,outcaps.szPname,-1,szName,256,0,0);
841                         add_or_update_card(m,szName,-1,item,MS_SND_CARD_CAP_PLAYBACK);
842 #endif
843                 }
844         }
845 }
846
847 static void deactivate_removed_cards(MSSndCardManager *m){
848         MSSndCard *card;
849         const MSList *elem=ms_snd_card_manager_get_list(m);
850         for(;elem!=NULL;elem=elem->next){
851                 card=(MSSndCard*)elem->data;
852                 if (strcmp(card->desc->driver_type, winsnd_card_desc.driver_type)==0){
853                         /*mark all cards as potentially removed, detect will check them immediately after */
854                         WinSndCard *d=(WinSndCard*)card->data;
855                         if (d->removed) card->capabilities=0;
856                 }
857         }
858 }
859
860 static void mark_as_removed(MSSndCardManager *m){
861         MSSndCard *card;
862         const MSList *elem=ms_snd_card_manager_get_list(m);
863         for(;elem!=NULL;elem=elem->next){
864                 card=(MSSndCard*)elem->data;
865                 if (strcmp(card->desc->driver_type, winsnd_card_desc.driver_type)==0){
866                         /*mark all cards as potentially removed, detect will check them immediately after */
867                         WinSndCard *d=(WinSndCard*)card->data;
868                         d->removed=1;
869                 }
870         }
871 }
872
873 static ms_thread_t poller_thread=NULL;
874 static bool_t poller_running=TRUE;
875
876 static void * new_device_polling_thread(void *ignore){
877         MSSndCardManager *m;
878         /*check for new devices every 5 seconds*/
879         while(poller_running){
880                 ms_sleep(5);
881                 if (poller_running){
882                         m=ms_snd_card_manager_get();
883                         if(!m) break;
884                         mark_as_removed(m);
885                         _winsndcard_detect(m);
886                         deactivate_removed_cards(m);
887                 }
888         }
889         return NULL;
890 }
891
892 static void stop_poller(){
893         poller_running=FALSE;
894         ms_thread_join(poller_thread,NULL);
895         poller_thread=NULL;
896 }
897
898 static void winsndcard_unload(MSSndCardManager *m){
899         stop_poller();
900 }
901
902 static void winsndcard_detect(MSSndCardManager *m){
903         _winsndcard_detect(m);
904         if (poller_thread==NULL)
905                 ms_thread_create(&poller_thread,NULL,new_device_polling_thread,NULL);
906 }
907
908 typedef struct WinSnd{
909         int dev_id;
910         HWAVEIN indev;
911         HWAVEOUT outdev;
912         WAVEFORMATEX wfx;
913         WAVEHDR hdrs_read[WINSND_NBUFS];
914         WAVEHDR hdrs_write[WINSND_OUT_NBUFS];
915         queue_t rq;
916         ms_mutex_t mutex;
917         uint64_t bytes_read;
918         unsigned int nbufs_playing;
919         bool_t running;
920
921         int32_t stat_input;
922         int32_t stat_output;
923         int32_t stat_notplayed;
924
925         int32_t stat_minimumbuffer;
926
927         queue_t write_rq;
928 #ifndef DISABLE_SPEEX
929         SpeexPreprocessState *pst;
930         int pst_frame_size;
931 #endif
932         int ready;
933         int workaround; /* workaround for opticon audio device */
934 }WinSnd;
935
936 static void winsnd_apply_settings(WinSnd *d){
937         d->wfx.nBlockAlign=d->wfx.nChannels*d->wfx.wBitsPerSample/8;
938         d->wfx.nAvgBytesPerSec=d->wfx.nSamplesPerSec*d->wfx.nBlockAlign;
939 }
940
941
942 /*#define _TRUE_TIME*/
943 #ifndef _TRUE_TIME
944 static uint64_t winsnd_get_cur_time( void *data){
945         WinSnd *d=(WinSnd*)data;
946         uint64_t curtime=(d->bytes_read*1000)/(uint64_t)d->wfx.nAvgBytesPerSec;
947         /* ms_debug("winsnd_get_cur_time: bytes_read=%u return %lu\n",d->bytes_read,(unsigned long)curtime); */
948         return curtime;
949 }
950 #endif
951
952
953 static void winsnd_init(MSFilter *f){
954         WinSnd *d=(WinSnd *)ms_new0(WinSnd,1);
955         d->wfx.wFormatTag = WAVE_FORMAT_PCM;
956         d->wfx.cbSize = 0;
957         d->wfx.nAvgBytesPerSec = 16000;
958         d->wfx.nBlockAlign = 2;
959         d->wfx.nChannels = 1;
960         d->wfx.nSamplesPerSec = 8000;
961         d->wfx.wBitsPerSample = 16;
962         qinit(&d->rq);
963         qinit(&d->write_rq);
964 #ifndef DISABLE_SPEEX
965         d->pst=NULL;
966         d->pst_frame_size=0;
967 #endif
968         d->ready=0;
969         d->workaround=0;
970         ms_mutex_init(&d->mutex,NULL);
971         f->data=d;
972
973         d->stat_input=0;
974         d->stat_output=0;
975         d->stat_notplayed=0;
976         d->stat_minimumbuffer=WINSND_MINIMUMBUFFER;
977 }
978
979 static void winsnd_uninit(MSFilter *f){
980         WinSnd *d=(WinSnd*)f->data;
981         flushq(&d->rq,0);
982         flushq(&d->write_rq,0);
983 #ifndef DISABLE_SPEEX
984         if (d->pst!=NULL)
985                 speex_preprocess_state_destroy(d->pst);
986         d->pst=NULL;
987         d->pst_frame_size=0;
988 #endif
989         d->ready=0;
990         d->workaround=0;
991         ms_mutex_destroy(&d->mutex);
992         ms_free(f->data);
993 }
994
995 static void add_input_buffer(WinSnd *d, WAVEHDR *hdr, int buflen){
996         mblk_t *m=allocb(buflen,0);
997         MMRESULT mr;
998         memset(hdr,0,sizeof(*hdr));
999         if (buflen==0) ms_error("add_input_buffer: buflen=0 !");
1000         hdr->lpData=(LPSTR)m->b_wptr;
1001         hdr->dwBufferLength=buflen;
1002         hdr->dwFlags = 0;
1003         hdr->dwUser = (DWORD)m;
1004         mr = waveInPrepareHeader (d->indev,hdr,sizeof(*hdr));
1005         if (mr != MMSYSERR_NOERROR){
1006                 ms_error("waveInPrepareHeader() error");
1007                 return ;
1008         }
1009         mr=waveInAddBuffer(d->indev,hdr,sizeof(*hdr));
1010         if (mr != MMSYSERR_NOERROR){
1011                 ms_error("waveInAddBuffer() error");
1012                 return ;
1013         }
1014 }
1015
1016 static void CALLBACK 
1017 read_callback (HWAVEIN waveindev, UINT uMsg, DWORD dwInstance, DWORD dwParam1,
1018                            DWORD dwParam2)
1019 {
1020         WAVEHDR *wHdr=(WAVEHDR *) dwParam1;
1021         MSFilter *f=(MSFilter *)dwInstance;
1022         WinSnd *d=(WinSnd*)f->data;
1023         mblk_t *m;
1024         int bsize;
1025         switch (uMsg){
1026                 case WIM_OPEN:
1027                         ms_debug("read_callback : WIM_OPEN");
1028                         break;
1029                 case WIM_CLOSE:
1030                         ms_debug("read_callback : WIM_CLOSE");
1031                         break;
1032                 case WIM_DATA:
1033                         bsize=wHdr->dwBytesRecorded;
1034                         if (bsize<=0) {
1035 #if 0
1036                                 if (d->running==TRUE) /* avoid adding buffer back when calling waveInReset */
1037                                 {
1038                                         MMRESULT mr;
1039                                         mr=waveInAddBuffer(d->indev,wHdr,sizeof(*wHdr));
1040                                         if (mr != MMSYSERR_NOERROR){
1041                                                 ms_error("waveInAddBuffer() error");
1042                                                 return ;
1043                                         }
1044                                         ms_warning("read_callback : EMPTY DATA, WIM_DATA (%p,%i)",wHdr,bsize);
1045                                 }
1046                                 m=(mblk_t*)wHdr->dwUser;
1047                                 wHdr->dwUser=0;
1048                                 freemsg(m);
1049                                 return;
1050 #endif
1051                         }
1052
1053                         /* ms_warning("read_callback : WIM_DATA (%p,%i)",wHdr,bsize); */
1054                         m=(mblk_t*)wHdr->dwUser;
1055                         m->b_wptr+=bsize;
1056                         wHdr->dwUser=0;
1057                         ms_mutex_lock(&d->mutex);
1058                         putq(&d->rq,m);
1059                         ms_mutex_unlock(&d->mutex);
1060                         d->bytes_read+=wHdr->dwBufferLength;
1061                         d->stat_input++;
1062                         d->stat_input++;
1063 #ifdef WIN32_TIMERS
1064                         if (f->ticker->TimeEvent!=NULL)
1065                                 SetEvent(f->ticker->TimeEvent);
1066 #endif
1067                         break;
1068         }
1069 }
1070
1071
1072 static void winsnd_read_preprocess(MSFilter *f){
1073         WinSnd *d=(WinSnd*)f->data;
1074         MMRESULT mr;
1075         int i;
1076         int bsize;
1077         DWORD dwFlag;
1078
1079         d->stat_input=0;
1080         d->stat_output=0;
1081         d->stat_notplayed=0;
1082         d->stat_minimumbuffer=WINSND_MINIMUMBUFFER;
1083
1084         winsnd_apply_settings(d);
1085         /* Init Microphone device */
1086         dwFlag = CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT;
1087         mr = waveInOpen (&d->indev, d->dev_id, &d->wfx,
1088                 (DWORD) read_callback, (DWORD)f, dwFlag);
1089         if (mr != MMSYSERR_NOERROR)
1090         {
1091                 ms_error("Failed to prepare windows sound device. (waveInOpen:0x%i)", mr);
1092                 if (d->dev_id != WAVE_MAPPER)
1093                         dwFlag = WAVE_MAPPED | CALLBACK_FUNCTION;
1094                 mr = waveInOpen (&d->indev, d->dev_id, &d->wfx,
1095                         (DWORD) read_callback, (DWORD)f, dwFlag);
1096         }
1097         if (mr != MMSYSERR_NOERROR)
1098         {
1099                 ms_error("Failed to prepare windows sound device. (waveInOpen:0x%i)", mr);
1100                 mr = waveInOpen (&d->indev, WAVE_MAPPER, &d->wfx,
1101                         (DWORD) read_callback, (DWORD)f, CALLBACK_FUNCTION);
1102                 if (mr != MMSYSERR_NOERROR)
1103                 {
1104                         d->indev=NULL;
1105                         ms_error("Failed to prepare windows sound device. (waveInOpen:0x%i)", mr);
1106                         return ;
1107                 }
1108         }
1109 #ifndef _TRUE_TIME
1110         ms_mutex_lock(&f->ticker->lock);
1111         ms_ticker_set_time_func(f->ticker,winsnd_get_cur_time,d);
1112         ms_mutex_unlock(&f->ticker->lock);
1113 #endif
1114
1115         bsize=WINSND_NSAMPLES*d->wfx.nAvgBytesPerSec/8000;
1116         ms_debug("Using input buffers of %i bytes",bsize);
1117         for(i=0;i<WINSND_NBUFS;++i){
1118                 WAVEHDR *hdr=&d->hdrs_read[i];
1119                 add_input_buffer(d,hdr,bsize);
1120         }
1121         d->running=TRUE;
1122         mr=waveInStart(d->indev);
1123         if (mr != MMSYSERR_NOERROR){
1124                 ms_error("waveInStart() error");
1125 #ifndef _TRUE_TIME
1126                 ms_mutex_lock(&f->ticker->lock);
1127                 ms_ticker_set_time_func(f->ticker,NULL,NULL);
1128                 ms_mutex_unlock(&f->ticker->lock);
1129 #endif
1130                 return ;
1131         }
1132 }
1133
1134 static void winsnd_read_postprocess(MSFilter *f){
1135         WinSnd *d=(WinSnd*)f->data;
1136         MMRESULT mr;
1137         int i;
1138 #ifndef _TRUE_TIME
1139         ms_mutex_lock(&f->ticker->lock);
1140         ms_ticker_set_time_func(f->ticker,NULL,NULL);
1141         ms_mutex_unlock(&f->ticker->lock);
1142 #endif
1143         d->running=FALSE;
1144         mr=waveInStop(d->indev);
1145         if (mr != MMSYSERR_NOERROR){
1146                 ms_error("waveInStop() error");
1147                 return ;
1148         }
1149         mr=waveInReset(d->indev);
1150         if (mr != MMSYSERR_NOERROR){
1151                 ms_error("waveInReset() error");
1152                 return ;
1153         }
1154         for(i=0;i<WINSND_NBUFS;++i){
1155                 WAVEHDR *hdr=&d->hdrs_read[i];
1156                 if (hdr->dwFlags & WHDR_PREPARED)
1157                 {
1158                         mr = waveInUnprepareHeader(d->indev,hdr,sizeof (*hdr));
1159                         if (mr != MMSYSERR_NOERROR){
1160                                 ms_error("waveInUnPrepareHeader() error");
1161                         }
1162                 }
1163         }
1164         mr = waveInClose(d->indev);
1165         if (mr != MMSYSERR_NOERROR){
1166                 ms_error("waveInClose() error");
1167                 return ;
1168         }
1169
1170         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);
1171         flushq(&d->rq,0);
1172 }
1173
1174 static void winsnd_read_process(MSFilter *f){
1175         WinSnd *d=(WinSnd*)f->data;
1176         mblk_t *m;
1177         int i;
1178         ms_mutex_lock(&d->mutex);
1179         while((m=getq(&d->rq))!=NULL){
1180                 ms_queue_put(f->outputs[0],m);
1181         }
1182         ms_mutex_unlock(&d->mutex);
1183         for(i=0;i<WINSND_NBUFS;++i){
1184                 WAVEHDR *hdr=&d->hdrs_read[i];
1185                 if (hdr->dwUser==0) {
1186                         MMRESULT mr;
1187                         mr=waveInUnprepareHeader(d->indev,hdr,sizeof(*hdr));
1188                         if (mr!=MMSYSERR_NOERROR)
1189                                 ms_warning("winsnd_read_process: Fail to unprepare header!");
1190                         add_input_buffer(d,hdr,hdr->dwBufferLength);
1191                 }
1192         }
1193 }
1194
1195 static void CALLBACK
1196 write_callback(HWAVEOUT outdev, UINT uMsg, DWORD dwInstance,
1197                            DWORD dwParam1, DWORD dwParam2)
1198 {
1199         WAVEHDR *hdr=(WAVEHDR *) dwParam1;
1200         WinSnd *d=(WinSnd*)dwInstance;
1201
1202         switch (uMsg){
1203                 case WOM_OPEN:
1204                         break;
1205                 case WOM_CLOSE:
1206                 case WOM_DONE:
1207                         if (hdr){
1208                                 d->nbufs_playing--;
1209                         }
1210                         if (d->stat_output==0)
1211                         {
1212                                 d->stat_input=1; /* reset */
1213                                 d->stat_notplayed=0;
1214                         }
1215                         d->stat_output++;
1216                         break;
1217         }
1218 }
1219
1220 static void winsnd_write_preprocess(MSFilter *f){
1221         WinSnd *d=(WinSnd*)f->data;
1222         MMRESULT mr;
1223         DWORD dwFlag;
1224         int i;
1225
1226         d->stat_input=0;
1227         d->stat_output=0;
1228         d->stat_notplayed=0;
1229         d->stat_minimumbuffer=WINSND_MINIMUMBUFFER;
1230
1231         winsnd_apply_settings(d);
1232         /* Init Microphone device */
1233         dwFlag = CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT;
1234         mr = waveOutOpen (&d->outdev, d->dev_id, &d->wfx,
1235                 (DWORD) write_callback, (DWORD)d, dwFlag);
1236         if (mr != MMSYSERR_NOERROR)
1237         {
1238                 ms_error("Failed to open windows sound device %i. (waveOutOpen:0x%i)",d->dev_id, mr);
1239                 if (d->dev_id != WAVE_MAPPER)
1240                         dwFlag = WAVE_MAPPED | CALLBACK_FUNCTION;
1241                 mr = waveOutOpen (&d->outdev, d->dev_id, &d->wfx,
1242                         (DWORD) write_callback, (DWORD)d, dwFlag);
1243         }
1244         if (mr != MMSYSERR_NOERROR)
1245         {
1246                 ms_error("Failed to open windows sound device %i. (waveOutOpen:0x%i)",d->dev_id, mr);
1247                 mr = waveOutOpen (&d->outdev, WAVE_MAPPER, &d->wfx,
1248                         (DWORD) write_callback, (DWORD)d, CALLBACK_FUNCTION);
1249                 if (mr != MMSYSERR_NOERROR)
1250                 {
1251                         ms_error("Failed to open windows sound device %i. (waveOutOpen:0x%i)",d->dev_id, mr);
1252                         d->outdev=NULL;
1253                         return ;
1254                 }
1255         }
1256         for(i=0;i<WINSND_OUT_NBUFS;++i){
1257                 WAVEHDR *hdr=&d->hdrs_write[i];
1258                 hdr->dwFlags=0;
1259                 hdr->dwUser=0;
1260         }
1261 }
1262
1263 static void winsnd_write_postprocess(MSFilter *f){
1264         WinSnd *d=(WinSnd*)f->data;
1265         MMRESULT mr;
1266         int i;
1267         if (d->outdev==NULL) return;
1268         mr=waveOutReset(d->outdev);
1269         if (mr != MMSYSERR_NOERROR){
1270                 ms_error("waveOutReset() error");
1271                 return ;
1272         }
1273         for(i=0;i<WINSND_OUT_NBUFS;++i){
1274                 WAVEHDR *hdr=&d->hdrs_write[i];
1275                 mblk_t *old;
1276                 if (hdr->dwFlags & WHDR_DONE){
1277                         mr=waveOutUnprepareHeader(d->outdev,hdr,sizeof(*hdr));
1278                         if (mr != MMSYSERR_NOERROR){
1279                                 ms_error("waveOutUnprepareHeader error");
1280                         }
1281                         old=(mblk_t*)hdr->dwUser;
1282                         if (old) freemsg(old);
1283                         hdr->dwUser=0;
1284                 }
1285         }
1286         mr=waveOutClose(d->outdev);
1287         if (mr != MMSYSERR_NOERROR){
1288                 ms_error("waveOutClose() error");
1289                 return ;
1290         }
1291         ms_message("Shutting down sound device (playing: %i) (d->write_rq.q_mcount=%i) (input-output: %i) (notplayed: %i)", d->nbufs_playing, d->write_rq.q_mcount, d->stat_input - d->stat_output, d->stat_notplayed);
1292         flushq(&d->write_rq,0);
1293         d->ready=0;
1294         d->workaround=0;
1295
1296 #ifndef DISABLE_SPEEX
1297         if (d->pst!=NULL)
1298                 speex_preprocess_state_destroy(d->pst);
1299         d->pst=NULL;
1300         d->pst_frame_size=0;
1301 #endif
1302 }
1303
1304 static void winsnd_write_process(MSFilter *f){
1305         WinSnd *d=(WinSnd*)f->data;
1306         mblk_t *m,*old;
1307         MMRESULT mr;
1308         int i;
1309         int discarded=0;
1310         int possible_size=0;
1311
1312         if (d->outdev==NULL) {
1313                 ms_queue_flush(f->inputs[0]);
1314                 return;
1315         }
1316
1317         while((m=ms_queue_get(f->inputs[0]))!=NULL){
1318                 possible_size = msgdsize(m);
1319 #ifndef DISABLE_SPEEX
1320                 if (d->pst_frame_size==0)
1321                 {
1322                         d->pst_frame_size=possible_size;
1323
1324                         d->pst = speex_preprocess_state_init(d->pst_frame_size/2, d->wfx.nSamplesPerSec);
1325                         if (d->pst!=NULL) {
1326                                 float f;
1327                                 i=1;
1328                                 speex_preprocess_ctl(d->pst, SPEEX_PREPROCESS_SET_VAD, &i);
1329                                 i=0;
1330                                 speex_preprocess_ctl(d->pst, SPEEX_PREPROCESS_SET_DENOISE, &i);
1331                                 i=0;
1332                                 speex_preprocess_ctl(d->pst, SPEEX_PREPROCESS_SET_AGC, &i);
1333                                 f=8000;
1334                                 speex_preprocess_ctl(d->pst, SPEEX_PREPROCESS_SET_AGC_LEVEL, &f);
1335                                 i=0;
1336                                 speex_preprocess_ctl(d->pst, SPEEX_PREPROCESS_SET_DEREVERB, &i);
1337                         }
1338                 }
1339 #endif
1340
1341                 putq(&d->write_rq,m);
1342         }
1343
1344 #ifdef AMD_HACK
1345         /* too many sound card are crappy on windows... */
1346         d->stat_minimumbuffer=15;
1347         if (d->wfx.nSamplesPerSec>=32000) /* better results for high rates */
1348                 d->stat_minimumbuffer=8;
1349 #endif
1350
1351         if (d->wfx.nSamplesPerSec>=32000) /* better results for high rates */
1352         {
1353                 if (d->nbufs_playing+d->write_rq.q_mcount<4)
1354                 {
1355                         d->ready=0;
1356                 }
1357         }
1358         else
1359         {
1360                 if (d->nbufs_playing+d->write_rq.q_mcount<7)
1361                 {
1362                         d->ready=0;
1363                 }
1364         }
1365 #if defined(WCE_OPTICON_WORKAROUND)
1366         if (d->workaround==0)
1367         {
1368                 d->workaround=1;
1369                 Sleep(WCE_OPTICON_WORKAROUND);
1370         }
1371 #endif
1372
1373         while((m=peekq(&d->write_rq))!=NULL){
1374
1375 #ifndef DISABLE_SPEEX
1376                 int vad=1;
1377                 if (d->pst!=NULL && msgdsize(m)==d->pst_frame_size && d->pst_frame_size<=4096)
1378                 {
1379                         char tmp[4096];
1380                         memcpy(tmp, m->b_rptr, msgdsize(m));
1381                         vad = speex_preprocess(d->pst, (short*)tmp, NULL);
1382
1383                         if (d->ready==0)
1384                         {
1385                                 if (vad==0)
1386                                 {
1387                                         int missing;
1388                                         missing = 10 - d->write_rq.q_mcount - d->nbufs_playing;
1389                                         if (d->wfx.nSamplesPerSec>=32000) /* better results for high rates */
1390                                                 missing = 6 - d->write_rq.q_mcount - d->nbufs_playing;
1391
1392                                         ms_message("WINSND trouble: inserting %i silence", missing);
1393                                         while(missing>0)
1394                                         {
1395                                                 old=dupb(m);
1396                                                 putq(&d->write_rq,old);
1397                                                 missing--;
1398                                         }
1399                                 }
1400                                 d->ready=1;
1401                         }
1402                 }
1403 #else
1404                 if (d->ready==0)
1405                 {
1406                         int missing;
1407                         missing = 10 - d->write_rq.q_mcount - d->nbufs_playing;
1408                         if (d->wfx.nSamplesPerSec>=32000) /* better results for high rates */
1409                                 missing = 6 - d->write_rq.q_mcount - d->nbufs_playing;
1410                         ms_message("WINSND trouble: inserting %i silence", missing);
1411                         while(missing>0)
1412                         {
1413                                 old=dupb(m);
1414                                 putq(&d->write_rq,old);
1415                                 missing--;
1416                         }
1417                         d->ready=1;
1418                 }
1419 #endif
1420
1421                 for(i=0;i<d->stat_minimumbuffer;++i){
1422                         WAVEHDR *hdr=&d->hdrs_write[i];
1423                         if (hdr->dwFlags & WHDR_DONE){
1424                                 old=(mblk_t*)hdr->dwUser;
1425                                 mr=waveOutUnprepareHeader(d->outdev,hdr,sizeof(*hdr));
1426                                 if (mr != MMSYSERR_NOERROR){
1427                                         ms_error("waveOutUnprepareHeader error");
1428                                 }
1429                                 freemsg(old);
1430                                 hdr->dwUser=0;
1431                         }
1432                         if (hdr->dwUser==0){
1433                                 hdr->lpData=(LPSTR)m->b_rptr;
1434                                 hdr->dwBufferLength=msgdsize(m);
1435                                 hdr->dwFlags = 0;
1436                                 hdr->dwUser = (DWORD)m;
1437                                 mr = waveOutPrepareHeader(d->outdev,hdr,sizeof(*hdr));
1438                                 if (mr != MMSYSERR_NOERROR){
1439                                         ms_error("waveOutPrepareHeader() error");
1440                                         getq(&d->write_rq);
1441                                         freemsg(m);
1442                                         discarded++;
1443                                         d->stat_notplayed++;
1444                                         break;
1445                                 }
1446                                 mr=waveOutWrite(d->outdev,hdr,sizeof(*hdr));
1447                                 if (mr != MMSYSERR_NOERROR){
1448                                         ms_error("waveOutWrite() error");
1449                                         getq(&d->write_rq);
1450                                         freemsg(m);
1451                                         discarded++;
1452                                         d->stat_notplayed++;
1453                                         break;
1454                                 }else {
1455                                         getq(&d->write_rq);
1456                                         d->nbufs_playing++;
1457                                         /* ms_debug("waveOutWrite() done"); */
1458                                 }
1459                                 break;
1460                         }
1461                 }
1462                 if (i==d->stat_minimumbuffer){
1463                         /* ms_error("winsnd_write_process: All buffers are busy."); */
1464 #ifndef DISABLE_SPEEX
1465                         if (d->pst==NULL)
1466                         {
1467                                 /* initial behavior (detection in process?) */
1468                                 getq(&d->write_rq);
1469                                 freemsg(m);
1470                                 discarded++;
1471                                 d->stat_notplayed++;
1472                         }
1473                         else
1474                         {
1475                                 if (vad==0)
1476                                 {
1477                                         getq(&d->write_rq);
1478                                         freemsg(m);
1479                                         ms_message("WINSND trouble: silence removed");
1480                                         discarded++;
1481                                         d->stat_notplayed++;
1482                                 }
1483                         }
1484 #else
1485                         getq(&d->write_rq);
1486                         freemsg(m);
1487                         discarded++;
1488                         d->stat_notplayed++;
1489 #endif
1490
1491                         break;
1492                 }
1493         }
1494 }
1495
1496 static int get_rate(MSFilter *f, void *arg){
1497         WinSnd *d=(WinSnd*)f->data;
1498         *((int*)arg)=d->wfx.nSamplesPerSec;
1499         return 0;
1500 }
1501
1502 static int set_rate(MSFilter *f, void *arg){
1503         WinSnd *d=(WinSnd*)f->data;
1504         d->wfx.nSamplesPerSec=*((int*)arg);
1505         d->wfx.nSamplesPerSec=44100;
1506         return 0;
1507 }
1508
1509 static int set_nchannels(MSFilter *f, void *arg){
1510         WinSnd *d=(WinSnd*)f->data;
1511         d->wfx.nChannels=*((int*)arg);
1512         return 0;
1513 }
1514
1515 static int winsnd_get_stat_input(MSFilter *f, void *arg){
1516         WinSnd *d=(WinSnd*)f->data;
1517         return d->stat_input;
1518 }
1519
1520 static int winsnd_get_stat_ouptut(MSFilter *f, void *arg){
1521         WinSnd *d=(WinSnd*)f->data;
1522
1523         return d->stat_output;
1524 }
1525
1526 static int winsnd_get_stat_discarded(MSFilter *f, void *arg){
1527         WinSnd *d=(WinSnd*)f->data;
1528
1529         return d->stat_notplayed;
1530 }
1531
1532 static MSFilterMethod winsnd_methods[]={
1533         {       MS_FILTER_GET_SAMPLE_RATE       , get_rate      },
1534         {       MS_FILTER_SET_SAMPLE_RATE       , set_rate      },
1535         {       MS_FILTER_SET_NCHANNELS         , set_nchannels },
1536         {       MS_FILTER_GET_STAT_INPUT, winsnd_get_stat_input },
1537         {       MS_FILTER_GET_STAT_OUTPUT, winsnd_get_stat_ouptut },
1538         {       MS_FILTER_GET_STAT_DISCARDED, winsnd_get_stat_discarded },
1539         {       0                               , NULL          }
1540 };
1541
1542 MSFilterDesc winsnd_read_desc={
1543         MS_WINSND_READ_ID,
1544         "MMERead",
1545         "MME capture filter for Windows",
1546         MS_FILTER_OTHER,
1547         NULL,
1548         0,
1549         1,
1550         winsnd_init,
1551         winsnd_read_preprocess,
1552         winsnd_read_process,
1553         winsnd_read_postprocess,
1554         winsnd_uninit,
1555         winsnd_methods
1556 };
1557
1558
1559 MSFilterDesc winsnd_write_desc={
1560         MS_WINSND_WRITE_ID,
1561         "MMEWrite",
1562         "MME playback filter for Windows",
1563         MS_FILTER_OTHER,
1564         NULL,
1565         1,
1566         0,
1567         winsnd_init,
1568         winsnd_write_preprocess,
1569         winsnd_write_process,
1570         winsnd_write_postprocess,
1571         winsnd_uninit,
1572         winsnd_methods
1573 };
1574
1575 MSFilter *ms_winsnd_read_new(MSSndCard *card){
1576         MSFilter *f=ms_filter_new_from_desc(&winsnd_read_desc);
1577         WinSndCard *wc=(WinSndCard*)card->data;
1578         WinSnd *d=(WinSnd*)f->data;
1579         d->dev_id=wc->in_devid;
1580         return f;
1581 }
1582
1583
1584 MSFilter *ms_winsnd_write_new(MSSndCard *card){
1585         MSFilter *f=ms_filter_new_from_desc(&winsnd_write_desc);
1586         WinSndCard *wc=(WinSndCard*)card->data;
1587         WinSnd *d=(WinSnd*)f->data;
1588         d->dev_id=wc->out_devid;
1589         return f;
1590 }
1591
1592 MS_FILTER_DESC_EXPORT(winsnd_read_desc)
1593 MS_FILTER_DESC_EXPORT(winsnd_write_desc)