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 #include "mediastreamer2/mssndcard.h"
21 #include "mediastreamer2/msfilter.h"
22 #include "mediastreamer2/msticker.h"
30 #if defined(_WIN32_WCE)
31 //#define DISABLE_SPEEX
32 //#define WCE_OPTICON_WORKAROUND 1000
36 #define WINSND_NBUFS 10
37 #define WINSND_OUT_DELAY 0.100
38 #define WINSND_OUT_NBUFS 20
39 #define WINSND_NSAMPLES 320
40 #define WINSND_MINIMUMBUFFER 5
42 static MSFilter *ms_winsnd_read_new(MSSndCard *card);
43 static MSFilter *ms_winsnd_write_new(MSSndCard *card);
45 typedef struct WinSndCard{
50 static void winsndcard_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent){
51 MMRESULT mr = MMSYSERR_NOERROR;
52 DWORD dwVolume = 0xFFFF;
53 dwVolume = ((0xFFFF) * percent) / 100;
56 case MS_SND_CARD_MASTER:
57 /*mr = waveOutSetVolume(d->waveoutdev, dwVolume); */
58 if (mr != MMSYSERR_NOERROR)
60 ms_warning("Failed to set master volume. (waveOutSetVolume:0x%i)", mr);
64 case MS_SND_CARD_CAPTURE:
66 case MS_SND_CARD_PLAYBACK:
69 ms_warning("winsnd_card_set_level: unsupported command.");
73 static int winsndcard_get_level(MSSndCard *card, MSSndCardMixerElem e){
75 case MS_SND_CARD_MASTER:
76 /*mr=waveOutGetVolume(d->waveoutdev, &dwVolume);*/
77 /* Transform to 0 to 100 scale*/
78 /*dwVolume = (dwVolume *100) / (0xFFFF);*/
81 case MS_SND_CARD_CAPTURE:
83 case MS_SND_CARD_PLAYBACK:
86 ms_warning("winsnd_card_get_level: unsupported command.");
92 static void winsndcard_set_source(MSSndCard *card, MSSndCardCapture source){
97 case MS_SND_CARD_LINE:
102 static void winsndcard_init(MSSndCard *card){
103 WinSndCard *c=(WinSndCard *)ms_new(WinSndCard,1);
107 static void winsndcard_uninit(MSSndCard *card){
111 static void winsndcard_detect(MSSndCardManager *m);
112 static MSSndCard *winsndcard_dup(MSSndCard *obj);
114 MSSndCardDesc winsnd_card_desc={
118 winsndcard_set_level,
119 winsndcard_get_level,
120 winsndcard_set_source,
129 static MSSndCard *winsndcard_dup(MSSndCard *obj){
130 MSSndCard *card=ms_snd_card_new(&winsnd_card_desc);
131 card->name=ms_strdup(obj->name);
132 card->data=ms_new(WinSndCard,1);
133 memcpy(card->data,obj->data,sizeof(WinSndCard));
137 static MSSndCard *winsndcard_new(const char *name, int in_dev, int out_dev, unsigned cap){
138 MSSndCard *card=ms_snd_card_new(&winsnd_card_desc);
139 WinSndCard *d=(WinSndCard*)card->data;
140 card->name=ms_strdup(name);
142 d->out_devid=out_dev;
143 card->capabilities=cap;
147 static void add_or_update_card(MSSndCardManager *m, const char *name, int indev, int outdev, unsigned int capability){
149 const MSList *elem=ms_snd_card_manager_get_list(m);
150 for(;elem!=NULL;elem=elem->next){
151 card=(MSSndCard*)elem->data;
152 if (strcmp(card->name,name)==0){
153 /*update already entered card */
154 WinSndCard *d=(WinSndCard*)card->data;
155 card->capabilities|=capability;
164 /* add this new card:*/
165 ms_snd_card_manager_add_card(m,winsndcard_new(name,indev,outdev,capability));
168 static void winsndcard_detect(MSSndCardManager *m){
169 MMRESULT mr = NOERROR;
170 unsigned int nOutDevices = waveOutGetNumDevs ();
171 unsigned int nInDevices = waveInGetNumDevs ();
174 if (nOutDevices>nInDevices)
175 nInDevices = nOutDevices;
177 for (item = 0; item < nInDevices; item++){
181 mr = waveInGetDevCaps (item, &incaps, sizeof (WAVEINCAPS));
182 if (mr == MMSYSERR_NOERROR)
184 #if defined(_WIN32_WCE)
186 snprintf(card, sizeof(card), "Input card %i", item);
187 add_or_update_card(m,card,item,-1,MS_SND_CARD_CAP_CAPTURE);
188 /* _tprintf(L"new card: %s", incaps.szPname); */
190 add_or_update_card(m,incaps.szPname,item,-1,MS_SND_CARD_CAP_CAPTURE);
193 mr = waveOutGetDevCaps (item, &outcaps, sizeof (WAVEOUTCAPS));
194 if (mr == MMSYSERR_NOERROR)
196 #if defined(_WIN32_WCE)
198 snprintf(card, sizeof(card), "Output card %i", item);
199 add_or_update_card(m,card,-1,item,MS_SND_CARD_CAP_PLAYBACK);
200 /* _tprintf(L"new card: %s", outcaps.szPname); */
202 add_or_update_card(m,outcaps.szPname,-1,item,MS_SND_CARD_CAP_PLAYBACK);
209 typedef struct WinSnd{
214 WAVEHDR hdrs_read[WINSND_NBUFS];
215 WAVEHDR hdrs_write[WINSND_OUT_NBUFS];
218 unsigned int bytes_read;
219 unsigned int nbufs_playing;
226 int32_t stat_notplayed;
228 int32_t stat_minimumbuffer;
230 int workaround; /* workaround for opticon audio device */
234 static void winsnd_apply_settings(WinSnd *d){
235 d->wfx.nBlockAlign=d->wfx.nChannels*d->wfx.wBitsPerSample/8;
236 d->wfx.nAvgBytesPerSec=d->wfx.nSamplesPerSec*d->wfx.nBlockAlign;
241 static uint64_t winsnd_get_cur_time( void *data){
242 WinSnd *d=(WinSnd*)data;
243 uint64_t curtime=((uint64_t)d->bytes_read*1000)/(uint64_t)d->wfx.nAvgBytesPerSec;
244 /* ms_debug("winsnd_get_cur_time: bytes_read=%u return %lu\n",d->bytes_read,(unsigned long)curtime); */
250 static void winsnd_init(MSFilter *f){
251 WinSnd *d=(WinSnd *)ms_new0(WinSnd,1);
252 d->wfx.wFormatTag = WAVE_FORMAT_PCM;
254 d->wfx.nAvgBytesPerSec = 16000;
255 d->wfx.nBlockAlign = 2;
256 d->wfx.nChannels = 1;
257 d->wfx.nSamplesPerSec = 8000;
258 d->wfx.wBitsPerSample = 16;
263 ms_mutex_init(&d->mutex,NULL);
269 d->stat_minimumbuffer=WINSND_MINIMUMBUFFER;
272 static void winsnd_uninit(MSFilter *f){
273 WinSnd *d=(WinSnd*)f->data;
278 ms_mutex_destroy(&d->mutex);
282 static void add_input_buffer(WinSnd *d, WAVEHDR *hdr, int buflen){
283 mblk_t *m=allocb(buflen,0);
285 memset(hdr,0,sizeof(*hdr));
286 if (buflen==0) ms_error("add_input_buffer: buflen=0 !");
287 hdr->lpData=(LPSTR)m->b_wptr;
288 hdr->dwBufferLength=buflen;
290 hdr->dwUser = (DWORD)m;
291 mr = waveInPrepareHeader (d->indev,hdr,sizeof(*hdr));
292 if (mr != MMSYSERR_NOERROR){
293 ms_error("waveInPrepareHeader() error");
296 mr=waveInAddBuffer(d->indev,hdr,sizeof(*hdr));
297 if (mr != MMSYSERR_NOERROR){
298 ms_error("waveInAddBuffer() error");
304 read_callback (HWAVEIN waveindev, UINT uMsg, DWORD dwInstance, DWORD dwParam1,
307 WAVEHDR *wHdr=(WAVEHDR *) dwParam1;
308 MSFilter *f=(MSFilter *)dwInstance;
309 WinSnd *d=(WinSnd*)f->data;
314 ms_debug("read_callback : WIM_OPEN");
317 ms_debug("read_callback : WIM_CLOSE");
320 bsize=wHdr->dwBytesRecorded;
322 /* ms_warning("read_callback : WIM_DATA (%p,%i)",wHdr,bsize); */
323 m=(mblk_t*)wHdr->dwUser;
326 ms_mutex_lock(&d->mutex);
328 ms_mutex_unlock(&d->mutex);
329 d->bytes_read+=wHdr->dwBufferLength;
333 if (f->ticker->TimeEvent!=NULL)
334 SetEvent(f->ticker->TimeEvent);
341 static void winsnd_read_preprocess(MSFilter *f){
342 WinSnd *d=(WinSnd*)f->data;
351 d->stat_minimumbuffer=WINSND_MINIMUMBUFFER;
353 winsnd_apply_settings(d);
354 /* Init Microphone device */
355 dwFlag = CALLBACK_FUNCTION;
356 if (d->dev_id != WAVE_MAPPER)
357 dwFlag = WAVE_MAPPED | CALLBACK_FUNCTION;
358 mr = waveInOpen (&d->indev, d->dev_id, &d->wfx,
359 (DWORD) read_callback, (DWORD)f, dwFlag);
360 if (mr != MMSYSERR_NOERROR)
362 ms_error("Failed to prepare windows sound device. (waveInOpen:0x%i)", mr);
363 mr = waveInOpen (&d->indev, WAVE_MAPPER, &d->wfx,
364 (DWORD) read_callback, (DWORD)f, CALLBACK_FUNCTION);
365 if (mr != MMSYSERR_NOERROR)
368 ms_error("Failed to prepare windows sound device. (waveInOpen:0x%i)", mr);
372 bsize=WINSND_NSAMPLES*d->wfx.nAvgBytesPerSec/8000;
373 ms_debug("Using input buffers of %i bytes",bsize);
374 for(i=0;i<WINSND_NBUFS;++i){
375 WAVEHDR *hdr=&d->hdrs_read[i];
376 add_input_buffer(d,hdr,bsize);
379 mr=waveInStart(d->indev);
380 if (mr != MMSYSERR_NOERROR){
381 ms_error("waveInStart() error");
385 ms_ticker_set_time_func(f->ticker,winsnd_get_cur_time,d);
389 static void winsnd_read_postprocess(MSFilter *f){
390 WinSnd *d=(WinSnd*)f->data;
394 ms_ticker_set_time_func(f->ticker,NULL,NULL);
397 mr=waveInStop(d->indev);
398 if (mr != MMSYSERR_NOERROR){
399 ms_error("waveInStop() error");
402 mr=waveInReset(d->indev);
403 if (mr != MMSYSERR_NOERROR){
404 ms_error("waveInReset() error");
407 for(i=0;i<WINSND_NBUFS;++i){
408 WAVEHDR *hdr=&d->hdrs_read[i];
409 if (hdr->dwFlags & WHDR_PREPARED)
411 mr = waveInUnprepareHeader(d->indev,hdr,sizeof (*hdr));
412 if (mr != MMSYSERR_NOERROR){
413 ms_error("waveInUnPrepareHeader() error");
417 mr = waveInClose(d->indev);
418 if (mr != MMSYSERR_NOERROR){
419 ms_error("waveInClose() error");
423 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);
427 static void winsnd_read_process(MSFilter *f){
428 WinSnd *d=(WinSnd*)f->data;
431 ms_mutex_lock(&d->mutex);
432 while((m=getq(&d->rq))!=NULL){
433 ms_queue_put(f->outputs[0],m);
435 ms_mutex_unlock(&d->mutex);
436 for(i=0;i<WINSND_NBUFS;++i){
437 WAVEHDR *hdr=&d->hdrs_read[i];
438 if (hdr->dwUser==0) {
440 mr=waveInUnprepareHeader(d->indev,hdr,sizeof(*hdr));
441 if (mr!=MMSYSERR_NOERROR)
442 ms_warning("winsnd_read_process: Fail to unprepare header!");
443 add_input_buffer(d,hdr,hdr->dwBufferLength);
449 write_callback(HWAVEOUT outdev, UINT uMsg, DWORD dwInstance,
450 DWORD dwParam1, DWORD dwParam2)
452 WAVEHDR *hdr=(WAVEHDR *) dwParam1;
453 WinSnd *d=(WinSnd*)dwInstance;
463 if (d->stat_output==0)
465 d->stat_input=1; /* reset */
473 static void winsnd_write_preprocess(MSFilter *f){
474 WinSnd *d=(WinSnd*)f->data;
482 d->stat_minimumbuffer=WINSND_MINIMUMBUFFER;
484 winsnd_apply_settings(d);
485 /* Init Microphone device */
486 dwFlag = CALLBACK_FUNCTION;
487 if (d->dev_id != WAVE_MAPPER)
488 dwFlag = WAVE_MAPPED | CALLBACK_FUNCTION;
489 mr = waveOutOpen (&d->outdev, d->dev_id, &d->wfx,
490 (DWORD) write_callback, (DWORD)d, dwFlag);
491 if (mr != MMSYSERR_NOERROR)
493 ms_error("Failed to open windows sound device %i. (waveOutOpen:0x%i)",d->dev_id, mr);
494 mr = waveOutOpen (&d->outdev, WAVE_MAPPER, &d->wfx,
495 (DWORD) write_callback, (DWORD)d, CALLBACK_FUNCTION);
496 if (mr != MMSYSERR_NOERROR)
498 ms_error("Failed to open windows sound device %i. (waveOutOpen:0x%i)",d->dev_id, mr);
503 for(i=0;i<WINSND_OUT_NBUFS;++i){
504 WAVEHDR *hdr=&d->hdrs_write[i];
513 static void winsnd_write_postprocess(MSFilter *f){
514 WinSnd *d=(WinSnd*)f->data;
517 if (d->outdev==NULL) return;
518 mr=waveOutReset(d->outdev);
519 if (mr != MMSYSERR_NOERROR){
520 ms_error("waveOutReset() error");
523 for(i=0;i<WINSND_OUT_NBUFS;++i){
524 WAVEHDR *hdr=&d->hdrs_write[i];
526 if (hdr->dwFlags & WHDR_DONE){
527 mr=waveOutUnprepareHeader(d->outdev,hdr,sizeof(*hdr));
528 if (mr != MMSYSERR_NOERROR){
529 ms_error("waveOutUnprepareHeader error");
531 old=(mblk_t*)hdr->dwUser;
532 if (old) freemsg(old);
536 mr=waveOutClose(d->outdev);
537 if (mr != MMSYSERR_NOERROR){
538 ms_error("waveOutClose() error");
545 static void playout_buf(WinSnd *d, WAVEHDR *hdr, mblk_t *m){
547 hdr->dwUser=(DWORD)m;
548 hdr->lpData=(LPSTR)m->b_rptr;
549 hdr->dwBufferLength=msgdsize(m);
551 mr = waveOutPrepareHeader(d->outdev,hdr,sizeof(*hdr));
552 if (mr != MMSYSERR_NOERROR){
553 ms_error("waveOutPrepareHeader() error");
556 mr=waveOutWrite(d->outdev,hdr,sizeof(*hdr));
557 if (mr != MMSYSERR_NOERROR){
558 ms_error("waveOutWrite() error");
565 static void winsnd_write_process(MSFilter *f){
566 WinSnd *d=(WinSnd*)f->data;
570 if (d->outdev==NULL) {
571 ms_queue_flush(f->inputs[0]);
575 ms_warning("nbufs_playing=%i",d->nbufs_playing);
576 if (d->nbufs_playing>0){
577 ms_queue_flush(f->inputs[0]);
580 else d->overrun=FALSE;
583 int outcurbuf=d->outcurbuf % WINSND_OUT_NBUFS;
584 WAVEHDR *hdr=&d->hdrs_write[outcurbuf];
585 old=(mblk_t*)hdr->dwUser;
587 int tmpsize=WINSND_OUT_DELAY*d->wfx.nAvgBytesPerSec;
588 mblk_t *tmp=allocb(tmpsize,0);
589 memset(tmp->b_wptr,0,tmpsize);
590 tmp->b_wptr+=tmpsize;
591 playout_buf(d,hdr,tmp);
593 d->nsamples+=WINSND_OUT_DELAY*d->wfx.nSamplesPerSec;
596 m=ms_queue_get(f->inputs[0]);
598 d->nsamples+=msgdsize(m)/d->wfx.nBlockAlign;
599 /*if the output buffer has finished to play, unprepare it*/
600 if (hdr->dwFlags & WHDR_DONE){
601 mr=waveOutUnprepareHeader(d->outdev,hdr,sizeof(*hdr));
602 if (mr != MMSYSERR_NOERROR){
603 ms_error("waveOutUnprepareHeader error");
611 /* a free wavheader */
612 playout_buf(d,hdr,m);
614 /* no more free wavheader, overrun !*/
615 ms_warning("WINSND overrun, restarting");
618 waveOutReset(d->outdev);
624 static int set_rate(MSFilter *f, void *arg){
625 WinSnd *d=(WinSnd*)f->data;
626 d->wfx.nSamplesPerSec=*((int*)arg);
630 static int set_nchannels(MSFilter *f, void *arg){
631 WinSnd *d=(WinSnd*)f->data;
632 d->wfx.nChannels=*((int*)arg);
636 static int winsnd_get_stat_input(MSFilter *f, void *arg){
637 WinSnd *d=(WinSnd*)f->data;
638 return d->stat_input;
641 static int winsnd_get_stat_ouptut(MSFilter *f, void *arg){
642 WinSnd *d=(WinSnd*)f->data;
644 return d->stat_output;
647 static int winsnd_get_stat_discarded(MSFilter *f, void *arg){
648 WinSnd *d=(WinSnd*)f->data;
650 return d->stat_notplayed;
653 static MSFilterMethod winsnd_methods[]={
654 { MS_FILTER_SET_SAMPLE_RATE , set_rate },
655 { MS_FILTER_SET_NCHANNELS , set_nchannels },
656 { MS_FILTER_GET_STAT_INPUT, winsnd_get_stat_input },
657 { MS_FILTER_GET_STAT_OUTPUT, winsnd_get_stat_ouptut },
658 { MS_FILTER_GET_STAT_DISCARDED, winsnd_get_stat_discarded },
662 MSFilterDesc winsnd_read_desc={
665 "Sound capture filter for Windows Sound drivers",
671 winsnd_read_preprocess,
673 winsnd_read_postprocess,
679 MSFilterDesc winsnd_write_desc={
682 "Sound playback filter for Windows Sound drivers",
688 winsnd_write_preprocess,
689 winsnd_write_process,
690 winsnd_write_postprocess,
695 MSFilter *ms_winsnd_read_new(MSSndCard *card){
696 MSFilter *f=ms_filter_new_from_desc(&winsnd_read_desc);
697 WinSndCard *wc=(WinSndCard*)card->data;
698 WinSnd *d=(WinSnd*)f->data;
699 d->dev_id=wc->in_devid;
704 MSFilter *ms_winsnd_write_new(MSSndCard *card){
705 MSFilter *f=ms_filter_new_from_desc(&winsnd_write_desc);
706 WinSndCard *wc=(WinSndCard*)card->data;
707 WinSnd *d=(WinSnd*)f->data;
708 d->dev_id=wc->out_devid;
712 MS_FILTER_DESC_EXPORT(winsnd_read_desc)
713 MS_FILTER_DESC_EXPORT(winsnd_write_desc)