From 536bc482cf4664773e216ecc266237ee0b792c6d Mon Sep 17 00:00:00 2001
From: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
Date: Fri, 30 May 2008 10:37:15 -0400
Subject: [PATCH] Reduce volume of all audio streams connected to Pulseaudio on
 incoming calls

Reduce volume to 10%
TODO: restore volume when hangup
---
 sflphone-gtk/src/audioconf.c | 91 ++++++++++++++++++++----------------
 src/audio/alsalayer.cpp      |  6 ++-
 src/audio/alsalayer.h        |  2 +-
 src/audio/audiolayer.h       |  2 +
 src/audio/audiostream.h      | 76 ++++++++++++++++++++++++++++--
 src/audio/pulselayer.cpp     | 72 +++++++++++++++++++++++++---
 src/audio/pulselayer.h       |  9 +++-
 src/managerimpl.cpp          |  6 ++-
 8 files changed, 210 insertions(+), 54 deletions(-)

diff --git a/sflphone-gtk/src/audioconf.c b/sflphone-gtk/src/audioconf.c
index b983a328e3..374a4f33c6 100644
--- a/sflphone-gtk/src/audioconf.c
+++ b/sflphone-gtk/src/audioconf.c
@@ -137,35 +137,37 @@ config_window_fill_output_audio_device_list()
   void
 select_active_output_audio_device()
 {
+  if( SHOW_ALSA_CONF )
+  {
 
-  GtkTreeModel* model;
-  GtkTreeIter iter;
-  gchar** devices;
-  int currentDeviceIndex;
-  int deviceIndex;
-
-  // Select active output device on server
-  //devices = dbus_get_current_audio_devices_index();
-  currentDeviceIndex = 0;//atoi(devices[0]);
-  printf(_("audio device index for output = %d\n"), currentDeviceIndex);
-  model = gtk_combo_box_get_model(GTK_COMBO_BOX(output));
-
-  // Find the currently set output device
-  gtk_tree_model_get_iter_first(model, &iter);
-  do {
-    gtk_tree_model_get(model, &iter, 1, &deviceIndex, -1);
-    if(deviceIndex == currentDeviceIndex)
-    {
-      // Set current iteration the active one
-      gtk_combo_box_set_active_iter(GTK_COMBO_BOX(output), &iter);
-      return;
-    }
-  } while(gtk_tree_model_iter_next(model, &iter));
-
-  // No index was found, select first one
-  g_print("Warning : No active output device found");
-  gtk_combo_box_set_active(GTK_COMBO_BOX(output), 0);
-
+    GtkTreeModel* model;
+    GtkTreeIter iter;
+    gchar** devices;
+    int currentDeviceIndex;
+    int deviceIndex;
+
+    // Select active output device on server
+    devices = dbus_get_current_audio_devices_index();
+    currentDeviceIndex = atoi(devices[0]);
+    printf(_("audio device index for output = %d\n"), currentDeviceIndex);
+    model = gtk_combo_box_get_model(GTK_COMBO_BOX(output));
+
+    // Find the currently set output device
+    gtk_tree_model_get_iter_first(model, &iter);
+    do {
+      gtk_tree_model_get(model, &iter, 1, &deviceIndex, -1);
+      if(deviceIndex == currentDeviceIndex)
+      {
+	// Set current iteration the active one
+	gtk_combo_box_set_active_iter(GTK_COMBO_BOX(output), &iter);
+	return;
+      }
+    } while(gtk_tree_model_iter_next(model, &iter));
+
+    // No index was found, select first one
+    g_print("Warning : No active output device found");
+    gtk_combo_box_set_active(GTK_COMBO_BOX(output), 0);
+  }
 }
 
 /**
@@ -188,7 +190,6 @@ config_window_fill_input_audio_device_list()
   //int c = 0;
   for(audioDevice = list; *list; list++)
   {
-    g_print("dbasbasdfbzfb\n");
     index = dbus_get_audio_device_index( *list );
     gtk_list_store_append(inputlist, &iter);
     gtk_list_store_set(inputlist, &iter, 0, *list, 1, index, -1);
@@ -203,7 +204,9 @@ config_window_fill_input_audio_device_list()
   void
 select_active_input_audio_device()
 {
-
+  if( SHOW_ALSA_CONF)
+{
+  
   GtkTreeModel* model;
   GtkTreeIter iter;
   gchar** devices;
@@ -230,7 +233,7 @@ select_active_input_audio_device()
   // No index was found, select first one
   g_print("Warning : No active input device found");
   gtk_combo_box_set_active(GTK_COMBO_BOX(input), 0);
-
+}
 }
 
 /**
@@ -264,19 +267,19 @@ select_active_output_audio_plugin()
 {
   GtkTreeModel* model;
   GtkTreeIter iter;
-  gchar* plugin;
+  gchar* pluginname;
   gchar* tmp;
 
   // Select active output device on server
-  plugin = dbus_get_current_audio_output_plugin();
-  tmp = plugin;
+  pluginname = dbus_get_current_audio_output_plugin();
+  tmp = pluginname;
   model = gtk_combo_box_get_model(GTK_COMBO_BOX(plugin));
 
   // Find the currently alsa plugin
   gtk_tree_model_get_iter_first(model, &iter);
   do {
-    gtk_tree_model_get(model, &iter, 0, &plugin , -1);
-    if( g_strcasecmp( tmp , plugin ) == 0 )
+    gtk_tree_model_get(model, &iter, 0, &pluginname , -1);
+    if( g_strcasecmp( tmp , pluginname ) == 0 )
     {
       // Set current iteration the active one
       gtk_combo_box_set_active_iter(GTK_COMBO_BOX(plugin), &iter);
@@ -600,16 +603,22 @@ GtkWidget* codecs_box()
   void
 select_audio_manager( void )
 {
+  g_print("audio manager selected\n");
   if( !SHOW_ALSA_CONF && !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(pulse) ) )
   {
+    g_print(" display alsa conf panel\n");
     alsabox = alsa_box();
     gtk_container_add( GTK_CONTAINER(alsa_conf ) , alsabox);
     gtk_widget_show( alsa_conf );    
   }
   else if( SHOW_ALSA_CONF && gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(pulse) ))
   {
+    g_print(" remove alsa conf panel\n");
     gtk_container_remove( GTK_CONTAINER(alsa_conf) , alsabox );
   }
+  else
+    g_print("alsa conf panel...nothing\n");
+
   gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(pulse) )? dbus_set_audio_manager( PULSEAUDIO ):dbus_set_audio_manager( ALSA );
 }
 
@@ -649,6 +658,7 @@ GtkWidget* alsa_box()
   gtk_box_pack_start( GTK_BOX(ret) , table , TRUE , TRUE , 1);
   gtk_widget_show(table);
 
+  g_print("plugin\n");
   item = gtk_label_new(_("ALSA plugin"));
   gtk_misc_set_alignment(GTK_MISC(item), 0, 0.5);
   gtk_table_attach(GTK_TABLE(table), item, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
@@ -657,7 +667,7 @@ GtkWidget* alsa_box()
   pluginlist = gtk_list_store_new(1, G_TYPE_STRING);
   config_window_fill_output_audio_plugin_list();
   plugin = gtk_combo_box_new_with_model(GTK_TREE_MODEL(pluginlist));
-  //select_active_output_audio_plugin();
+  select_active_output_audio_plugin();
   gtk_label_set_mnemonic_widget(GTK_LABEL(item), plugin);
   g_signal_connect(G_OBJECT(plugin), "changed", G_CALLBACK(select_output_audio_plugin), plugin);
 
@@ -670,6 +680,7 @@ GtkWidget* alsa_box()
 
   // Device : Output device
   // Create title label
+  g_print("output\n");
   item = gtk_label_new(_("Output"));
   gtk_misc_set_alignment(GTK_MISC(item), 0, 0.5);
   gtk_table_attach(GTK_TABLE(table), item, 1, 2, 2, 3, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
@@ -678,7 +689,7 @@ GtkWidget* alsa_box()
   outputlist = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
   config_window_fill_output_audio_device_list();
   output = gtk_combo_box_new_with_model(GTK_TREE_MODEL(outputlist));
-  //select_active_output_audio_device();
+  select_active_output_audio_device();
   gtk_label_set_mnemonic_widget(GTK_LABEL(item), output);
   g_signal_connect(G_OBJECT(output), "changed", G_CALLBACK(select_audio_output_device), output);
 
@@ -691,6 +702,7 @@ GtkWidget* alsa_box()
 
   // Device : Input device
   // Create title label
+  g_print("input\n");
   item = gtk_label_new(_("Input"));
   gtk_misc_set_alignment(GTK_MISC(item), 0, 0.5);
   gtk_table_attach(GTK_TABLE(table), item, 1, 2, 3, 4, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
@@ -699,7 +711,7 @@ GtkWidget* alsa_box()
   inputlist = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
   config_window_fill_input_audio_device_list();
   input = gtk_combo_box_new_with_model(GTK_TREE_MODEL(inputlist));
-  //select_active_input_audio_device();
+  select_active_input_audio_device();
   gtk_label_set_mnemonic_widget(GTK_LABEL(item), input);
   g_signal_connect(G_OBJECT(input), "changed", G_CALLBACK(select_audio_input_device), input);
 
@@ -712,6 +724,7 @@ GtkWidget* alsa_box()
 
   gtk_widget_show_all(ret);
 
+  g_print("done\n");
   return ret;
 }
 
diff --git a/src/audio/alsalayer.cpp b/src/audio/alsalayer.cpp
index 2763246187..c1ea3e63e0 100644
--- a/src/audio/alsalayer.cpp
+++ b/src/audio/alsalayer.cpp
@@ -220,6 +220,10 @@ AlsaLayer::isStreamStopped (void)
   return !(isStreamActive());
 }
 
+
+void 
+AlsaLayer::reducePulseAppsVolume( void ){}
+
 //////////////////////////////////////////////////////////////////////////////////////////////
 /////////////////   ALSA PRIVATE FUNCTIONS   ////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////////////////
@@ -334,7 +338,7 @@ AlsaLayer::open_device(std::string pcm_p, std::string pcm_c, int flag)
 
     _debugAlsa(" Opening playback device %s\n", pcm_p.c_str());
     if(err = snd_pcm_open(&_PlaybackHandle, pcm_p.c_str(),  SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) < 0){
-      _debugAlsa("Error while opening playback device %s\n",  pcm_c.c_str());
+      _debugAlsa("Error while opening playback device %s\n",  pcm_p.c_str());
       setErrorMessage( ALSA_PLAYBACK_DEVICE );
       return false;
     }
diff --git a/src/audio/alsalayer.h b/src/audio/alsalayer.h
index 858e0bba45..2b82bbe11c 100644
--- a/src/audio/alsalayer.h
+++ b/src/audio/alsalayer.h
@@ -183,7 +183,7 @@ class AlsaLayer : public AudioLayer {
 
     int putInCache( char code, void *buffer, int toCopy );
     int putMain(void* buffer, int toCopy);
-
+    void reducePulseAppsVolume( void );
   private:
 
     /**
diff --git a/src/audio/audiolayer.h b/src/audio/audiolayer.h
index 2f51adcb10..0b0f768167 100644
--- a/src/audio/audiolayer.h
+++ b/src/audio/audiolayer.h
@@ -183,6 +183,8 @@ class AudioLayer {
      */
     virtual std::string getAudioPlugin( void ) = 0; 
 
+    virtual void reducePulseAppsVolume( void ) = 0;
+  
     /**
      * Write accessor to the error state
      * @param error The error code
diff --git a/src/audio/audiostream.h b/src/audio/audiostream.h
index c420628d9b..d31c2df930 100644
--- a/src/audio/audiostream.h
+++ b/src/audio/audiostream.h
@@ -27,6 +27,9 @@
 #include "ringbuffer.h"
 #include "audioloop.h"
 
+/**
+ * This data structure contains the different king of audio streams available
+ */
 enum STREAM_TYPE {
   PLAYBACK_STREAM,
   CAPTURE_STREAM,
@@ -36,30 +39,95 @@ enum STREAM_TYPE {
 
 class AudioStream {
   public:
+    /**
+     * Constructor
+     * @param context The pulseaudio context
+     * @param type    The type of audio stream
+     * @param desc    The stream name
+     */ 
     AudioStream(pa_context* context , int type, std::string desc);
+    
+    /**
+     * Destructor
+     */   
     ~AudioStream();
 
+    /**
+     * Write data to the main abstraction ring buffer. 
+     * @param buffer The buffer containing the data to be played
+     * @param toCopy The number of samples, in bytes
+     * @return int The number of bytes played
+     */
     int putMain( void* buffer , int toCopy );
+
+    /**
+     * Write data to the urgent abstraction ring buffer. ( dtmf , double calls )
+     * @param buffer The buffer containing the data to be played
+     * @param toCopy The number of samples, in bytes
+     * @return int The number of bytes played
+     */
     int putUrgent( void* buffer , int toCopy );
 
+    /**
+     * Disconnect the pulseaudio stream
+     */
     void disconnect();
+    
+    /**
+     * Accessor: Get the pulseaudio stream object
+     * @return pa_stream* The stream
+     */
     pa_stream* pulseStream(){ return _audiostream; }
 
+    /**
+     * Accessor
+     * @return std::string  The stream name
+     */
+    std::string getStreamName( void ) { return _streamDescription; }
+
   private:
+    /**
+     * Create the audio stream into the given context
+     * @param c	The pulseaudio context
+     * @return pa_stream* The newly created audio stream
+     */
     pa_stream* createStream( pa_context* c ); 
 
+    /**
+     * Mandatory asynchronous callback on the audio stream state
+     */
     static void stream_state_callback( pa_stream* s, void* user_data );	
+    
+    /**
+     * Asynchronous callback on data processing ( write and read )
+     */
     static void audioCallback ( pa_stream* s, size_t bytes, void* userdata );
+    
+    /**
+     * Write data to the sound device
+     */
     void write( void );
 
+    /**
+     * The pulse audio object
+     */
+    pa_stream* _audiostream;
+    
+    /**
+     * The type of the stream
+     */
     int _streamType;
+    
+    /**
+     * The name of the stream
+     */
     std::string _streamDescription;
-
-
-    pa_stream* _audiostream;
+    
+    /**
+     * Streams parameters
+     */
     pa_stream_flags_t flag;
     pa_sample_spec sample_spec ;
-    //pa_channel_map channel_map;
     pa_volume_t volume;
 
 };
diff --git a/src/audio/pulselayer.cpp b/src/audio/pulselayer.cpp
index e783d91682..6dfd1fe27c 100644
--- a/src/audio/pulselayer.cpp
+++ b/src/audio/pulselayer.cpp
@@ -19,11 +19,6 @@
 
 #include "pulselayer.h"
 
-/*static pa_channel_map channel_map ;
-  static pa_stream_flags_t flag;
-  static pa_sample_spec sample_spec ;
-  static pa_volume_t volume;
-  */
 int framesPerBuffer = 2048;
 
   PulseLayer::PulseLayer(ManagerImpl* manager)
@@ -73,7 +68,8 @@ PulseLayer::connectPulseServer( void )
   }
 
   pa_threaded_mainloop_unlock( m );
-
+  serverinfo();
+  //muteAudioApps(99);
   _debug("Context creation done\n");
 }
 
@@ -331,3 +327,67 @@ PulseLayer::putInCache( char code, void *buffer, int toCopy )
   //pa_stream_finish_upload( cache->pulseStream() );
 }
 
+static void retrieve_server_info(pa_context *c, const pa_server_info *i, void *userdata)
+{
+  _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);  
+}
+
+static void retrieve_client_list(pa_context *c, const pa_client_info *i, int eol, void *userdata)
+{
+  _debug("end of list = %i\n", eol);
+  _debug("Clients Info: index : %i\n" , i->index);  
+  _debug("\t\tClient name : %s\n" , i->name);  
+  _debug("\t\tOwner module : %i\n" , i->owner_module);  
+  _debug("\t\tDriver : %s\n" , i->driver);  
+}
+
+static void retrieve_sink_list(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata)
+{
+  PulseLayer* pulse = (PulseLayer*) userdata;
+  AudioStream* s = pulse->getPlaybackStream();
+  //_debug("my app index = %d\n",pa_stream_get_index(pulse->getPlaybackStream()->pulseStream()));
+  if( !eol ){
+    _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); 
+    if( strcmp( i->name ,  s->getStreamName().c_str()) != 0)
+      pulse->reduceAppVolume( i->index , i->volume.channels);
+  }  
+}
+
+void
+PulseLayer::reducePulseAppsVolume( void )
+{
+  pa_context_get_sink_input_info_list( context , retrieve_sink_list , this );
+}
+
+void
+PulseLayer::serverinfo( void )
+{
+  pa_context_get_server_info( context , retrieve_server_info , NULL );
+}
+
+static void on_success(pa_context *c, int success, void *userdata)
+{
+  _debug("Operation successfull \n");
+}
+
+void
+PulseLayer::reduceAppVolume( int index , int channels )
+{
+  pa_cvolume volume;
+  pa_volume_t vol = PA_VOLUME_NORM;
+  vol /= 10; 
+  
+  pa_cvolume_set( &volume , channels , vol);
+  _debug("Mute Index %i\n" , index);
+  pa_context_set_sink_input_volume( context, index, &volume, on_success, this) ;
+  
+}
+
+
diff --git a/src/audio/pulselayer.h b/src/audio/pulselayer.h
index 7a6bea3562..9ce53a0d11 100644
--- a/src/audio/pulselayer.h
+++ b/src/audio/pulselayer.h
@@ -80,7 +80,6 @@ class PulseLayer : public AudioLayer {
     static void overflow ( pa_stream* s, void* userdata );
     static void underflow ( pa_stream* s, void* userdata );
     static void stream_state_callback( pa_stream* s, void* user_data );	
-
     static void context_state_callback( pa_context* c, void* user_data );	
 
     /**
@@ -121,6 +120,11 @@ class PulseLayer : public AudioLayer {
      */
     std::string getAudioPlugin( void ) { return "default"; }
     
+    void reducePulseAppsVolume( void );
+    void reduceAppVolume( int index , int channels ); 
+
+    AudioStream* getPlaybackStream(){ return playback;}
+    AudioStream* getRecordStream(){ return record;}
 
   private:
     /**
@@ -137,6 +141,8 @@ class PulseLayer : public AudioLayer {
 
     void connectPulseServer( void );
 
+    void serverinfo( void );
+
     /** Ringbuffers for data */
     RingBuffer _mainSndRingBuffer;
     RingBuffer _urgentRingBuffer;
@@ -145,7 +151,6 @@ class PulseLayer : public AudioLayer {
     /** PulseAudio streams and context */
     pa_context* context;
     pa_threaded_mainloop* m;
-
     AudioStream* playback;
     AudioStream* record;
     AudioStream* cache;
diff --git a/src/managerimpl.cpp b/src/managerimpl.cpp
index eb92a78c16..61c81f1235 100644
--- a/src/managerimpl.cpp
+++ b/src/managerimpl.cpp
@@ -580,6 +580,10 @@ ManagerImpl::incomingCall(Call* call, const AccountID& accountId)
     from.append(">");
   }
   _dbus->getCallManager()->incomingCall(accountId, call->getCallId(), from);
+
+  // Reduce volume of the other pulseaudio-connected audio applications
+  _audiodriver->reducePulseAppsVolume();
+  
   return true;
 }
 
@@ -1592,7 +1596,7 @@ ManagerImpl::switchAudioManager( void )
   int numCardIn  = getConfigInt( AUDIO , ALSA_CARD_ID_IN );
   int numCardOut = getConfigInt( AUDIO , ALSA_CARD_ID_OUT );
 
-  _debug("Deleting current layer...\n");
+  _debug("Deleting current layer... \n" );
   _audiodriver->closeLayer();
   delete _audiodriver; _audiodriver = NULL;
   
-- 
GitLab