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 <speex/speex_preprocess.h>
22 #include "mediastreamer2/mssndcard.h"
23 #include "mediastreamer2/msfilter.h"
25 #include "portaudio.h"
27 MSFilter *ms_pasnd_read_new(MSSndCard *card);
28 MSFilter *ms_pasnd_write_new(MSSndCard *card);
30 typedef struct PASndData{
34 char waveoutbuffer[30][3200];
44 MSBufferizer * bufferizer;
49 SpeexPreprocessState *pst;
52 int SpeakerCallback( const void *inputBuffer, void *outputBuffer,
53 unsigned long framesPerBuffer,
54 const PaStreamCallbackTimeInfo* timeInfo,
55 PaStreamCallbackFlags statusFlags,
58 PASndData *device = (PASndData*)userData;
59 uint8_t *wtmpbuff=NULL;
61 int ovfl = (device->rate/8000)*320*6;
63 memset(outputBuffer,0, framesPerBuffer*2);
64 if (!device->read_started && !device->write_started)
69 wtmpbuff=(uint8_t*)alloca(framesPerBuffer*2);
71 memset(outputBuffer,0, framesPerBuffer*2);
73 ms_mutex_lock(&device->mutex);
75 /* remove extra buffer when latency is increasing:
76 this often happen with USB device */
77 if (device->bufferizer->size>=ovfl){
78 ms_warning("Extra data for sound card (total:%i %ims)",
79 device->bufferizer->size, (device->bufferizer->size*20)/320);
80 err=ms_bufferizer_read(device->bufferizer,wtmpbuff, framesPerBuffer*2);
81 err=ms_bufferizer_read(device->bufferizer,wtmpbuff, framesPerBuffer*2);
82 err=ms_bufferizer_read(device->bufferizer,wtmpbuff, framesPerBuffer*2);
83 err=ms_bufferizer_read(device->bufferizer,wtmpbuff, framesPerBuffer*2);
84 err=ms_bufferizer_read(device->bufferizer,wtmpbuff, framesPerBuffer*2);
85 ms_warning("Extra data for sound card removed (total:%i %ims)",
86 device->bufferizer->size, (device->bufferizer->size*20)/320);
89 err=ms_bufferizer_read(device->bufferizer,wtmpbuff,framesPerBuffer*2);
90 ms_mutex_unlock(&device->mutex);
91 if (err==framesPerBuffer*2)
93 memcpy (outputBuffer, wtmpbuff, framesPerBuffer*2);
99 int WaveInCallback( const void *inputBuffer, void *outputBuffer,
100 unsigned long framesPerBuffer,
101 const PaStreamCallbackTimeInfo* timeInfo,
102 PaStreamCallbackFlags statusFlags,
105 PASndData *device = (PASndData*)userData;
107 if (!device->read_started && !device->write_started)
112 ms_mutex_lock(&device->mutex);
113 if (device->read_started)
117 if (rm==NULL) rm=allocb(framesPerBuffer*2,0);
118 memcpy(rm->b_wptr,inputBuffer, framesPerBuffer*2);
120 if (device->pst!=NULL)
122 vad = speex_preprocess(device->pst, (spx_int16_t *)rm->b_wptr, NULL);
125 ms_message("WaveInCallback : %d", vad);
129 rm->b_wptr+=framesPerBuffer*2;
131 putq(&device->rq,rm);
134 ms_mutex_unlock(&device->mutex);
139 static int pasnd_open(PASndData *device, int devnumber, int bits,int stereo, int rate, int *minsz)
141 PaStreamParameters outputParameters;
142 PaStreamParameters inputParameters;
145 const PaHostApiInfo *pa_hai = Pa_GetHostApiInfo(Pa_GetDefaultHostApi());
147 ms_warning("pasnd_open : opening default input device: name=%s (%i)",
148 pa_hai->name, pa_hai->defaultInputDevice);
149 ms_warning("pasnd_open : opening default output device name=%s (%i)",
150 pa_hai->name, pa_hai->defaultOutputDevice);
152 outputParameters.device = devnumber; /* default output device */
153 outputParameters.device = pa_hai->defaultOutputDevice;
154 outputParameters.channelCount = 1; /* stereo output */
155 outputParameters.sampleFormat = paInt16; /* 32 bit floating point output */
156 outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
157 outputParameters.hostApiSpecificStreamInfo = NULL;
160 &device->waveoutdev, /* stream */
162 &outputParameters, //
163 rate, // double sampleRate
164 160*(rate/8000), //unsigned long framesPerBuffer
166 SpeakerCallback, //PortAudioCallback *callback
167 (void *) device); //void *userData
169 if (err != paNoError)
171 ms_warning("Failed to open out device. (Pa_OpenDefaultStream:0x%i)", err);
175 inputParameters.device = devnumber; /* default input device */
176 inputParameters.device = pa_hai->defaultInputDevice;
177 inputParameters.channelCount = 1; /* stereo input */
178 inputParameters.sampleFormat = paInt16; /* 32 bit floating point input */
179 inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
180 inputParameters.hostApiSpecificStreamInfo = NULL;
183 &device->waveindev, //PortAudioStream** stream
184 &inputParameters, /* input param*/
185 NULL, /* output param */
186 rate, // double sampleRate
187 160*(rate/8000), //unsigned long framesPerBuffer
189 WaveInCallback, //PortAudioCallback *callback
190 (void *) device); //void *userData
193 if (err != paNoError)
195 ms_warning("Failed to open in device. (Pa_OpenDefaultStream:0x%i)", err);
199 err = Pa_StartStream( device->waveoutdev );
200 if( err != paNoError )
202 ms_warning("Failed to start out device. (Pa_StartStream:0x%i)", err);
206 device->pst = speex_preprocess_state_init((device->rate/8000 * 320)/2, device->rate);
207 if (device->pst!=NULL) {
210 speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_VAD, &i);
212 speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_DENOISE, &i);
214 speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_AGC, &i);
216 speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_AGC_LEVEL, &f);
218 speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_DEREVERB, &i);
220 speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);
222 speex_preprocess_ctl(device->pst, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);
225 err = Pa_StartStream( device->waveindev );
226 if( err != paNoError )
228 ms_warning("Failed to start in device: trying default device. (Pa_StartStream:0x%i)", err);
232 *minsz=device->rate/8000 * 320;
236 static void pasnd_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent)
238 PASndData *d=(PASndData*)card->data;
240 if (d->mixdev==NULL) return;
242 case MS_SND_CARD_MASTER:
245 case MS_SND_CARD_CAPTURE:
247 case MS_SND_CARD_PLAYBACK:
250 ms_warning("pasnd_card_set_level: unsupported command.");
255 static int pasnd_get_level(MSSndCard *card, MSSndCardMixerElem e)
257 PASndData *d=(PASndData*)card->data;
259 if (d->mixdev==NULL) return -1;
261 case MS_SND_CARD_MASTER:
264 case MS_SND_CARD_CAPTURE:
266 case MS_SND_CARD_PLAYBACK:
269 ms_warning("pasnd_card_get_level: unsupported command.");
275 static void pasnd_set_source(MSSndCard *card, MSSndCardCapture source)
277 PASndData *d=(PASndData*)card->data;
278 if (d->mixdev==NULL) return;
281 case MS_SND_CARD_MIC:
283 case MS_SND_CARD_LINE:
288 static void pasnd_init(MSSndCard *card){
289 PASndData *d=ms_new(PASndData,1);
290 memset(d, 0, sizeof(PASndData));
293 d->sound_err=-1; /* not opened */
294 d->read_started=FALSE;
295 d->write_started=FALSE;
300 d->bufferizer=ms_bufferizer_new();
301 ms_mutex_init(&d->mutex,NULL);
306 static void pasnd_uninit(MSSndCard *card){
307 PASndData *d=(PASndData*)card->data;
310 if (d->pcmdev!=NULL) ms_free(d->pcmdev);
311 if (d->mixdev!=NULL) ms_free(d->mixdev);
312 ms_bufferizer_destroy(d->bufferizer);
315 ms_mutex_destroy(&d->mutex);
318 speex_preprocess_state_destroy(d->pst);
323 #define DSP_NAME "/dev/dsp"
324 #define MIXER_NAME "/dev/mixer"
326 static void pasnd_detect(MSSndCardManager *m);
327 static MSSndCard *pasnd_duplicate(MSSndCard *obj);
329 MSSndCardDesc pasnd_card_desc={
344 static MSSndCard *pasnd_duplicate(MSSndCard *obj){
345 MSSndCard *card=ms_snd_card_new(&pasnd_card_desc);
346 PASndData *dcard=(PASndData*)card->data;
347 PASndData *dobj=(PASndData*)obj->data;
348 dcard->pcmdev=ms_strdup(dobj->pcmdev);
349 dcard->mixdev=ms_strdup(dobj->mixdev);
350 card->name=ms_strdup(obj->name);
354 static MSSndCard *pasnd_card_new(const char *pcmdev, const char *mixdev){
355 MSSndCard *card=ms_snd_card_new(&pasnd_card_desc);
356 PASndData *d=(PASndData*)card->data;
357 d->pcmdev=ms_strdup(pcmdev);
358 d->mixdev=ms_strdup(mixdev);
359 card->name=ms_strdup(pcmdev);
363 static void pasnd_detect(MSSndCardManager *m){
365 unsigned int numDevices;
366 const PaDeviceInfo *pdi;
371 err = Pa_Initialize();
372 if( err != paNoError )
374 ms_warning("PortAudio error: %s\n", Pa_GetErrorText( err ) );
378 numDevices = Pa_GetDeviceCount();
380 for( i=0; i<numDevices; i++ ) {
381 pdi = Pa_GetDeviceInfo( i );
385 snprintf(pcmdev,sizeof(pcmdev),"%s",pdi->name);
386 snprintf(mixdev,sizeof(mixdev),"%s",pdi->name);
389 card=pasnd_card_new(pcmdev,mixdev);
390 ms_snd_card_manager_add_card(m,card);
392 card=pasnd_card_new(pcmdev,mixdev);
393 ms_snd_card_manager_add_card(m,card);
398 static void pasnd_closedriver(PASndData *d)
400 if (d->sound_err==0) {
402 int err = Pa_StopStream( d->waveindev );
403 if( err != paNoError )
405 ms_warning("Failed to stop device. (Pa_StopStream:0x%i)", err);
408 err = Pa_CloseStream( d->waveindev);
409 if( err != paNoError )
411 ms_warning("failed to close recording sound card (Pa_CloseStream:0x%i)", err);
415 ms_message("successfully closed recording sound card");
418 err = Pa_StopStream( d->waveoutdev );
419 if( err != paNoError )
421 ms_warning("Failed to stop device. (Pa_StopStream:0x%i)", err);
424 err = Pa_CloseStream( d->waveoutdev );
425 if( err != paNoError )
427 ms_error("failed to stop recording sound card (Pa_CloseStream:0x%i)", err);
431 ms_message("successfully stopped recording sound card");
439 static void pasnd_start_r(MSSndCard *card){
440 PASndData *d=(PASndData*)card->data;
441 if (d->read_started==FALSE && d->write_started==FALSE){
443 d->read_started=TRUE;
444 d->sound_err=pasnd_open(d, 0, d->bits,d->stereo,d->rate,&bsize);
445 }else d->read_started=TRUE;
448 static void pasnd_stop_r(MSSndCard *card){
449 PASndData *d=(PASndData*)card->data;
450 d->read_started=FALSE;
451 if (d->write_started==FALSE){
452 /* ms_thread_join(d->thread,NULL); */
453 pasnd_closedriver(d);
457 static void pasnd_start_w(MSSndCard *card){
458 PASndData *d=(PASndData*)card->data;
459 if (d->read_started==FALSE && d->write_started==FALSE){
461 d->write_started=TRUE;
462 d->sound_err=pasnd_open(d, 0, d->bits,d->stereo,d->rate,&bsize);
464 d->write_started=TRUE;
468 static void pasnd_stop_w(MSSndCard *card){
469 PASndData *d=(PASndData*)card->data;
470 d->write_started=FALSE;
471 if (d->read_started==FALSE){
472 /* ms_thread_join(d->thread,NULL); */
473 pasnd_closedriver(d);
477 static mblk_t *pasnd_get(MSSndCard *card){
478 PASndData *d=(PASndData*)card->data;
480 ms_mutex_lock(&d->mutex);
482 ms_mutex_unlock(&d->mutex);
486 static void pasnd_put(MSSndCard *card, mblk_t *m){
487 PASndData *d=(PASndData*)card->data;
488 ms_mutex_lock(&d->mutex);
489 ms_bufferizer_put(d->bufferizer,m);
490 ms_mutex_unlock(&d->mutex);
494 static void pasnd_read_preprocess(MSFilter *f){
495 MSSndCard *card=(MSSndCard*)f->data;
499 static void pasnd_read_postprocess(MSFilter *f){
500 MSSndCard *card=(MSSndCard*)f->data;
504 static void pasnd_read_process(MSFilter *f){
505 MSSndCard *card=(MSSndCard*)f->data;
507 while((m=pasnd_get(card))!=NULL){
508 ms_queue_put(f->outputs[0],m);
512 static void pasnd_write_preprocess(MSFilter *f){
513 MSSndCard *card=(MSSndCard*)f->data;
517 static void pasnd_write_postprocess(MSFilter *f){
518 MSSndCard *card=(MSSndCard*)f->data;
522 static void pasnd_write_process(MSFilter *f){
523 MSSndCard *card=(MSSndCard*)f->data;
525 while((m=ms_queue_get(f->inputs[0]))!=NULL){
530 static int set_rate(MSFilter *f, void *arg){
531 MSSndCard *card=(MSSndCard*)f->data;
532 PASndData *d=(PASndData*)card->data;
533 d->rate=*((int*)arg);
537 static int set_nchannels(MSFilter *f, void *arg){
538 MSSndCard *card=(MSSndCard*)f->data;
539 PASndData *d=(PASndData*)card->data;
540 d->stereo=(*((int*)arg)==2);
544 static MSFilterMethod pasnd_methods[]={
545 { MS_FILTER_SET_SAMPLE_RATE , set_rate },
546 { MS_FILTER_SET_NCHANNELS , set_nchannels },
550 MSFilterDesc pasnd_read_desc={
553 "Sound capture filter for Port Audio Sound drivers",
559 pasnd_read_preprocess,
561 pasnd_read_postprocess,
567 MSFilterDesc pasnd_write_desc={
570 "Sound playback filter for Port Audio Sound drivers",
576 pasnd_write_preprocess,
578 pasnd_write_postprocess,
583 MSFilter *ms_pasnd_read_new(MSSndCard *card){
584 MSFilter *f=ms_filter_new_from_desc(&pasnd_read_desc);
590 MSFilter *ms_pasnd_write_new(MSSndCard *card){
591 MSFilter *f=ms_filter_new_from_desc(&pasnd_write_desc);
596 MS_FILTER_DESC_EXPORT(pasnd_read_desc)
597 MS_FILTER_DESC_EXPORT(pasnd_write_desc)