pulselayer.cpp 12.4 KB
Newer Older
1
/*
2
 *  Copyright (C) 2008 Savoir-Faire Linux inc.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

20
#include "pulselayer.h"
21

22
int framesPerBuffer = 2048;
23

Yun Liu's avatar
Yun Liu committed
24
25
int PulseLayer::streamState;

26
27
28
29
30
31
32
33
static  void audioCallback ( pa_stream* s, size_t bytes, void* userdata )
{ 
  assert( s && bytes );
  assert( bytes > 0 );
  static_cast<PulseLayer*>(userdata)->processData();
}
  
    PulseLayer::PulseLayer(ManagerImpl* manager)
Yun Liu's avatar
Yun Liu committed
34
35
36
37
38
  : AudioLayer( manager , PULSEAUDIO ) 
  , context(NULL)
  , m(NULL)
  , playback()
  , record()
39
{
Yun Liu's avatar
Yun Liu committed
40
  PulseLayer::streamState = 0;
Emmanuel Milou's avatar
Emmanuel Milou committed
41
  _debug("Pulse audio constructor: Create context\n");
42
43
44
45
46
}

// Destructor
PulseLayer::~PulseLayer (void) 
{ 
47
48
    closeLayer ();

Emmanuel Milou's avatar
Emmanuel Milou committed
49
50
    pa_context_disconnect( context );  
    pa_context_unref( context );
51
52
}

53
  void
54
PulseLayer::closeLayer( void )
Yun Liu's avatar
Yun Liu committed
55
{ 
56
57
  _debug(" Destroy pulselayer\n");
  
58
59
  playback->disconnect(); 
  record->disconnect();
Yun Liu's avatar
Yun Liu committed
60
61
62
63
 
  while(PulseLayer::streamState != 2)
    ;
  PulseLayer::streamState = 0; 
64
65
66

  //TODO  Remove this ugly hack
  sleep(2);
67
68
}

69
  void
70
PulseLayer::connectPulseAudioServer( void )
Emmanuel Milou's avatar
Emmanuel Milou committed
71
72
73
{
  pa_context_flags_t flag = PA_CONTEXT_NOAUTOSPAWN ;  

74
  pa_threaded_mainloop_lock( m );
Emmanuel Milou's avatar
Emmanuel Milou committed
75

76
  _debug("Connect the context to the server\n");
77
  pa_context_connect( context, NULL , flag , NULL ); 
Emmanuel Milou's avatar
Emmanuel Milou committed
78

79
80
  pa_context_set_state_callback(context, context_state_callback, this);
  pa_threaded_mainloop_wait( m );
Emmanuel Milou's avatar
Emmanuel Milou committed
81
82

  // Run the main loop
83
84
85
86
87
88
  if( pa_context_get_state( context ) != PA_CONTEXT_READY ){
    _debug("Error connecting to pulse audio server\n");
    pa_threaded_mainloop_unlock( m );
  }

  pa_threaded_mainloop_unlock( m );
Emmanuel Milou's avatar
Emmanuel Milou committed
89
  //serverinfo();
90
  //muteAudioApps(99);
Emmanuel Milou's avatar
Emmanuel Milou committed
91
92
93
  _debug("Context creation done\n");
}

94
void PulseLayer::context_state_callback( pa_context* c, void* user_data )
Emmanuel Milou's avatar
Emmanuel Milou committed
95
96
{
  _debug("The state of the context changed\n");
97
  PulseLayer* pulse = (PulseLayer*)user_data;
98
  assert(c && pulse->m);
Emmanuel Milou's avatar
Emmanuel Milou committed
99
100
101
102
103
104
105
  switch(pa_context_get_state(c)){
    case PA_CONTEXT_CONNECTING:
    case PA_CONTEXT_AUTHORIZING:
    case PA_CONTEXT_SETTING_NAME:
      _debug("Waiting....\n");
      break;
    case PA_CONTEXT_READY:
106
      pulse->createStreams( c );
Emmanuel Milou's avatar
Emmanuel Milou committed
107
108
109
110
111
112
113
      _debug("Connection to PulseAudio server established\n");	
      break;
    case PA_CONTEXT_TERMINATED:
      _debug("Context terminated\n");
      break;
    case PA_CONTEXT_FAILED:
    default:
114
115
116
117
      _debug(" Error : %s\n" , pa_strerror(pa_context_errno(c)));
      pulse->disconnectPulseAudioServer();
      exit(0);
      break;
Emmanuel Milou's avatar
Emmanuel Milou committed
118
119
120
  }
}

121
122
123
void PulseLayer::disconnectPulseAudioServer( void )
{
  if( playback )
Emmanuel Milou's avatar
Emmanuel Milou committed
124
    delete playback; playback=NULL;
125
126

  if( record )
Emmanuel Milou's avatar
Emmanuel Milou committed
127
    delete record; record=NULL;
128
129
}

130
131
132
  void
PulseLayer::createStreams( pa_context* c )
{
133
  playback = new AudioStream(c, PLAYBACK_STREAM, PLAYBACK_STREAM_NAME, _manager->getSpkrVolume());
134
  pa_stream_set_write_callback( playback->pulseStream() , audioCallback, this);
135
  //pa_stream_set_overflow_callback( playback->pulseStream() , overflow , this);
136
  record = new AudioStream(c, CAPTURE_STREAM, CAPTURE_STREAM_NAME , _manager->getMicVolume());
137
  pa_stream_set_read_callback( record->pulseStream() , audioCallback, this);
138
  //pa_stream_set_underflow_callback( record->pulseStream() , underflow , this);
Emmanuel Milou's avatar
Emmanuel Milou committed
139

140
141
142
143
  pa_threaded_mainloop_signal(m , 0);
}

  bool 
144
PulseLayer::openDevice(int indexIn UNUSED, int indexOut UNUSED, int sampleRate, int frameSize , int stream UNUSED, std::string plugin UNUSED) 
145
146
147
148
{
  _sampleRate = sampleRate;
  _frameSize = frameSize;	

149
150
  m = pa_threaded_mainloop_new();
  assert(m);
151

152
153
154
  if( pa_threaded_mainloop_start( m ) < 0  ){
    _debug("Failed starting the mainloop\n");
  }
155

156
157
158
  // Instanciate a context
  if( !(context = pa_context_new( pa_threaded_mainloop_get_api( m ) , "SFLphone" )))
    _debug("Error while creating the context\n");
159

160
  assert(context);
161

162
  connectPulseAudioServer();
163
164

  _debug("Connection Done!! \n");
Yun Liu's avatar
Yun Liu committed
165
  return true;
Emmanuel Milou's avatar
Emmanuel Milou committed
166
167
}

168
  void 
Emmanuel Milou's avatar
Emmanuel Milou committed
169
170
171
172
PulseLayer::closeCaptureStream( void )
{
}

173
  void 
Emmanuel Milou's avatar
Emmanuel Milou committed
174
175
176
177
PulseLayer::closePlaybackStream( void )
{
}

178
179
180
  int
PulseLayer::canGetMic()
{
181
  if( record )
182
    return  _micRingBuffer.AvailForGet();
183
184
  else
    return 0;
185
186
187
188
189
}

  int 
PulseLayer::getMic(void *buffer, int toCopy)
{
190
  if( record ){
191
    return _micRingBuffer.Get(buffer, toCopy, 100);
192
193
194
  }
  else
    return 0;
195
196
}

197
  void 
Emmanuel Milou's avatar
Emmanuel Milou committed
198
199
PulseLayer::startStream (void) 
{
200
  _micRingBuffer.flush();
Emmanuel Milou's avatar
Emmanuel Milou committed
201
  _debug("Start stream\n");
202
203
204
  pa_threaded_mainloop_lock(m);
  pa_stream_cork( record->pulseStream(), NULL, NULL, NULL);
  pa_threaded_mainloop_unlock(m);
Emmanuel Milou's avatar
Emmanuel Milou committed
205
206
}

207
  void 
Emmanuel Milou's avatar
Emmanuel Milou committed
208
209
210
PulseLayer::stopStream (void) 
{
  _debug("Stop stream\n");
211
212
213
  pa_stream_flush( playback->pulseStream(), NULL, NULL );
  pa_stream_flush( record->pulseStream(), NULL, NULL );
  flushMic();
Emmanuel Milou's avatar
Emmanuel Milou committed
214
215
  flushMain();
  flushUrgent();
Emmanuel Milou's avatar
Emmanuel Milou committed
216
217
}

218

219
220

  void 
221
PulseLayer::underflow ( pa_stream* s UNUSED,  void* userdata UNUSED )
222
223
224
225
226
227
{ 
  _debug("Buffer Underflow\n");
}


  void 
228
PulseLayer::overflow ( pa_stream* s, void* userdata UNUSED )
229
{ 
Yun Liu's avatar
Yun Liu committed
230
  //PulseLayer* pulse = (PulseLayer*) userdata;
231
232
  pa_stream_drop( s );
  pa_stream_trigger( s, NULL, NULL);
233
234
}

235
  void
236
PulseLayer::processData( void )
237
{
238
  // Handle the mic
239
240
241
  // We check if the stream is ready
  if( (record->pulseStream()) && pa_stream_get_state( record->pulseStream()) == PA_STREAM_READY) 
    readFromMic();
242

243
244
  // Handle the data for the speakers
  if( (playback->pulseStream()) && pa_stream_get_state( playback->pulseStream()) == PA_STREAM_READY){
245

246
247
    // If the playback buffer is full, we don't overflow it; wait for it to have free space
    if( pa_stream_writable_size(playback->pulseStream()) == 0 )
248
      return;
249

250
251
252
253
254
255
256
257
258
259
260
261
262
    writeToSpeaker();
  }
}

void PulseLayer::writeToSpeaker( void )
{   
  /** Bytes available in the urgent ringbuffer ( reserved for DTMF ) */
  int urgentAvail; 
  /** Bytes available in the regular ringbuffer ( reserved for voice ) */
  int normalAvail; 
  int toGet;
  int toPlay;

263
  SFLDataFormat* out;// = (SFLDataFormat*)pa_xmalloc(framesPerBuffer);
264
  urgentAvail = _urgentRingBuffer.AvailForGet();
265

266
267
  if (urgentAvail > 0) {
    // Urgent data (dtmf, incoming call signal) come first.		
Emmanuel Milou's avatar
Emmanuel Milou committed
268
    //_debug("Play urgent!: %i\e" , urgentAvail);
269
270
271
272
273
    toGet = (urgentAvail < (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? urgentAvail : framesPerBuffer * sizeof(SFLDataFormat);
    out =  (SFLDataFormat*)pa_xmalloc(toGet * sizeof(SFLDataFormat) );
    _urgentRingBuffer.Get(out, toGet, 100);
    pa_stream_write( playback->pulseStream() , out , toGet  , pa_xfree, 0 , PA_SEEK_RELATIVE);
    // Consume the regular one as well (same amount of bytes)
274
    _voiceRingBuffer.Discard(toGet);
275
276
277
278
279
280
  }
  else
  {
    AudioLoop* tone = _manager->getTelephoneTone();
    if ( tone != 0) {
      toGet = framesPerBuffer;
Emmanuel Milou's avatar
Idem    
Emmanuel Milou committed
281
282
283
      out =  (SFLDataFormat*)pa_xmalloc(toGet * sizeof(SFLDataFormat) );
      tone->getNext(out, toGet , 100);
      pa_stream_write( playback->pulseStream() , out , toGet  * sizeof(SFLDataFormat)   , pa_xfree, 0 , PA_SEEK_RELATIVE);
284
285
286
    } 
    if ( (tone=_manager->getTelephoneFile()) != 0 ) {
      toGet = framesPerBuffer;
287
      toPlay = ( (int)(toGet * sizeof(SFLDataFormat)) > framesPerBuffer )? framesPerBuffer : toGet * sizeof(SFLDataFormat) ;
288
289
290
291
292
293
      out =  (SFLDataFormat*)pa_xmalloc(toPlay);
      tone->getNext(out, toPlay/2 , 100);
      pa_stream_write( playback->pulseStream() , out , toPlay   , pa_xfree, 0 , PA_SEEK_RELATIVE) ; 
    } 
    else {
      out =  (SFLDataFormat*)pa_xmalloc(framesPerBuffer * sizeof(SFLDataFormat));
294
      normalAvail = _voiceRingBuffer.AvailForGet();
295
296
      toGet = (normalAvail < (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? normalAvail : framesPerBuffer * sizeof(SFLDataFormat);
      if (toGet) {
297
298
	    _voiceRingBuffer.Get(out, toGet, 100);
	    _voiceRingBuffer.Discard(toGet);
299
300
      } 
      else {
Emmanuel Milou's avatar
Emmanuel Milou committed
301
	    bzero(out, framesPerBuffer * sizeof(SFLDataFormat));
302
      }
303
304
      pa_stream_write( playback->pulseStream() , out , toGet  , NULL, 0 , PA_SEEK_RELATIVE);
      pa_xfree(out);
305
    }
306
  }
Emmanuel Milou's avatar
Emmanuel Milou committed
307

308
309
310
311
312
313
314
315
}
 
void PulseLayer::readFromMic( void )
{
  const char* data;
  size_t r;

  if( pa_stream_peek( record->pulseStream() , (const void**)&data , &r ) < 0 || !data ){
Emmanuel Milou's avatar
Emmanuel Milou committed
316
    //_debug("pa_stream_peek() failed: %s\n" , pa_strerror( pa_context_errno( context) ));
317
318
319
  }

  if( data != 0 ){
320
    _micRingBuffer.Put( (void*)data ,r, 100);
321
322
323
  }

  if( pa_stream_drop( record->pulseStream() ) < 0 ) {
Emmanuel Milou's avatar
Emmanuel Milou committed
324
    //_debug("pa_stream_drop() failed: %s\n" , pa_strerror( pa_context_errno( context) ));
325
  }
326
327
}

328
static void retrieve_server_info(pa_context *c UNUSED, const pa_server_info *i, void *userdata UNUSED)
329
330
331
332
333
334
335
{
  _debug("Server Info: Process owner : %s\n" , i->user_name);  
  _debug("\t\tServer name : %s - Server version = %s\n" , i->server_name, i->server_version);  
  _debug("\t\tDefault sink name : %s\n" , i->default_sink_name);  
  _debug("\t\tDefault source name : %s\n" , i->default_source_name);  
}

336
static void reduce_sink_list_cb(pa_context *c UNUSED, const pa_sink_input_info *i, int eol, void *userdata)
337
338
339
{
  PulseLayer* pulse = (PulseLayer*) userdata;
  if( !eol ){
340
341
342
343
    //_debug("Sink Info: index : %i\n" , i->index);  
    //_debug("\t\tClient : %i\n" , i->client); 
    //_debug("\t\tVolume : %i\n" , i->volume.values[0]); 
    //_debug("\t\tChannels : %i\n" , i->volume.channels); 
344
    if( strcmp( i->name , PLAYBACK_STREAM_NAME ) != 0)
345
      pulse->setSinkVolume( i->index , i->volume.channels, 10 );
346
347
348
  }  
}

349
static void restore_sink_list_cb(pa_context *c UNUSED, const pa_sink_input_info *i, int eol, void *userdata)
350
351
352
{
  PulseLayer* pulse = (PulseLayer*) userdata;
  if( !eol ){
353
354
355
356
357
    //_debug("Sink Info: index : %i\n" , i->index);  
    //_debug("\t\tSink name : -%s-\n" , i->name);  
    //_debug("\t\tClient : %i\n" , i->client); 
    //_debug("\t\tVolume : %i\n" , i->volume.values[0]); 
    //_debug("\t\tChannels : %i\n" , i->volume.channels); 
358
    if( strcmp( i->name , PLAYBACK_STREAM_NAME ) != 0)
359
      pulse->setSinkVolume( i->index , i->volume.channels, 100);
360
361
362
  }  
}

363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
static void set_playback_volume_cb(pa_context *c UNUSED, const pa_sink_input_info *i, int eol, void *userdata)
{
    PulseLayer* pulse;
    int volume;
  
    pulse = (PulseLayer*) userdata;
    volume = pulse->getSpkrVolume();

    if( !eol ){
        if( strcmp( i->name , PLAYBACK_STREAM_NAME ) == 0)
            pulse->setSinkVolume( i->index , i->volume.channels, volume );
    }  
}

static void set_capture_volume_cb(pa_context *c UNUSED, const pa_source_output_info *i, int eol, void *userdata)
{
    PulseLayer* pulse;
    int volume;
  
    pulse = (PulseLayer*) userdata;
    volume = pulse->getMicVolume();

    if( !eol ){
        if( strcmp( i->name , CAPTURE_STREAM_NAME ) == 0)
            pulse->setSourceVolume( i->index , i->channel_map.channels, volume );
    }  
}

391
  void
392
393
PulseLayer::reducePulseAppsVolume( void )
{
394
  pa_context_get_sink_input_info_list( context , reduce_sink_list_cb , this );
395
396
}

397
  void
398
399
PulseLayer::restorePulseAppsVolume( void )
{
400
  pa_context_get_sink_input_info_list( context , restore_sink_list_cb , this );
401
402
}

403
  void
404
405
406
407
408
409
PulseLayer::serverinfo( void )
{
  pa_context_get_server_info( context , retrieve_server_info , NULL );
}


410
void PulseLayer::setSinkVolume( int index, int channels, int volume )
411
{
412
413
414
415
416
417
418
    
    pa_cvolume cvolume;
    pa_volume_t vol = PA_VOLUME_NORM * ((double)volume / 100) ;

    pa_cvolume_set( &cvolume , channels , vol);
   _debug("Set sink volume of index %i\n" , index);
   pa_context_set_sink_input_volume( context, index, &cvolume, NULL, NULL) ;
419

420
421
}

422
void PulseLayer::setSourceVolume( int index, int channels, int volume )
423
{
424
425
426
427
428
429
430
    
    pa_cvolume cvolume;
    pa_volume_t vol = PA_VOLUME_NORM * ((double)volume / 100) ;

    pa_cvolume_set( &cvolume , channels , vol);
   _debug("Set source volume of index %i\n" , index);
    pa_context_set_source_volume_by_index(context, index, &cvolume, NULL, NULL);
431

432
433
}

434
435

void PulseLayer::setPlaybackVolume( int volume )
Emmanuel Milou's avatar
Emmanuel Milou committed
436
{
437
438
    setSpkrVolume( volume );
    pa_context_get_sink_input_info_list( context , set_playback_volume_cb , this );
Emmanuel Milou's avatar
Emmanuel Milou committed
439
440
}

441
442
443
444
445
void PulseLayer::setCaptureVolume( int volume )
{
    setMicVolume( volume );
    pa_context_get_source_output_info_list( context , set_capture_volume_cb , this );
}
446