1 #include "MafwGstRendererVolume.h"
4 #include <dbusconnectioneventloop.h>
9 // The default PulseAudioMainVolume DBus API socket
10 #define DEFAULT_ADDRESS "unix:path=/var/run/pulse/dbus-socket"
11 #define ROLE = "x-maemo";
13 #define PULSE_CORE_PATH "/org/pulseaudio/core1"
14 #define STREAM_RESTORE_PATH "/org/pulseaudio/stream_restore1"
15 #define STREAM_RESTORE_IF "org.PulseAudio.Ext.StreamRestore1"
16 #define GET_ENTRY_METHOD "GetEntryByName"
17 #define STREAM_RESTORE_IF_ENTRY STREAM_RESTORE_IF ".RestoreEntry"
18 #define STREAM_RESTORE_VOLUME_SIGNAL STREAM_RESTORE_IF_ENTRY ".VolumeUpdated"
20 #define ENTRY_NAME "sink-input-by-media-role:x-maemo"
21 //# 100% volume in Pulseaudio's native volume units.
22 const uint VOLUME_NORM = 0x10000;
23 //Place of mono channel at pulse audio's channel position enumeration
24 const uint VOLUME_CHANNEL_MONO = 0;
25 //Delay for attempting to reconnect to pulseaudio
26 const uint PULSE_RESTART_DELAY = 2000;
28 /********************************************************************
29 * MafwGstRendererVolume::MafwGstRendererVolume
30 ********************************************************************/
31 MafwGstRendererVolume::MafwGstRendererVolume(): m_currentVolume(0), m_pendingVolumeValue(0),
32 m_dbusConnection(0), m_objectPath(QString()), m_pendingCall(0)
34 qDebug() << __PRETTY_FUNCTION__;
36 connectToPulseAudio();
39 /********************************************************************
40 * MafwGstRendererVolume::connectToPulseAudio
41 ********************************************************************/
42 void MafwGstRendererVolume::connectToPulseAudio()
44 qDebug() << __PRETTY_FUNCTION__;
46 QByteArray address = qgetenv("PULSE_DBUS_SERVER");
48 if (address.isEmpty())
50 address = QByteArray(DEFAULT_ADDRESS);
53 dbus_error_init (&error);
57 DBUSConnectionEventLoop::removeConnection(m_dbusConnection);
58 dbus_connection_unref(m_dbusConnection);
62 m_dbusConnection = dbus_connection_open (address.constData(), &error);
64 if (dbus_error_is_set(&error))
66 qCritical() << "Unable to open dbus connection to pulse audio:" << error.message;
67 dbus_error_free (&error);
68 QTimer::singleShot(PULSE_RESTART_DELAY, this, SLOT(connectToPulseAudio()));
72 if (DBUSConnectionEventLoop::addConnection(m_dbusConnection))
74 dbus_connection_add_filter (
76 (DBusHandleMessageFunction) handleIncomingMessages,
79 getRestoreEntryForMediaRole();
83 qCritical() << "DBUSConnectionEventLoop failure";
84 dbus_connection_unref(m_dbusConnection);
90 /********************************************************************
91 * MafwGstRendererVolume::~MafwGstRendererVolume()
92 ********************************************************************/
93 MafwGstRendererVolume::~MafwGstRendererVolume()
97 dbus_pending_call_cancel(m_pendingCall);
99 DBUSConnectionEventLoop::removeConnection(m_dbusConnection);
100 dbus_connection_unref(m_dbusConnection);
102 /********************************************************************
103 * We need the object path for the RestoreEntry of the "x-maemo" role.
104 * GetEntryByName with parameter "sink-input-by-media-role:x-maemo" is used
106 ********************************************************************/
107 void MafwGstRendererVolume::getRestoreEntryForMediaRole()
109 qDebug() << __PRETTY_FUNCTION__;
113 dbus_error_init (&error);
115 msg = dbus_message_new_method_call(0,
119 const char *name = ENTRY_NAME;
120 dbus_message_append_args (msg,
121 DBUS_TYPE_STRING, &name,
125 DBusPendingCall *pending = 0;
126 //Return value taken for satisfying coverity tool
127 bool outOfMemory = dbus_connection_send_with_reply( m_dbusConnection, msg, &pending, -1 );
128 Q_UNUSED(outOfMemory);
129 m_pendingCall = pending;
133 qDebug() << __PRETTY_FUNCTION__ << "pending call sent!";
134 dbus_pending_call_set_notify( pending,
135 (DBusPendingCallNotifyFunction) getEntryReply,
138 dbus_message_unref(msg);
140 qDebug() << __PRETTY_FUNCTION__ << "exit";
143 /********************************************************************
144 * Now we have the RestoreEntry object path for "x-maemo". Next we just
145 * need to start listening for the VolumeUpdated signals from that object,
146 * and use the Volume property for controlling the role volume.
147 ********************************************************************/
148 void MafwGstRendererVolume::getEntryReply(DBusPendingCall *pending, MafwGstRendererVolume *self)
150 qDebug() << __PRETTY_FUNCTION__;
151 self->m_pendingCall = 0;
155 dbus_error_init(&error);
157 reply = dbus_pending_call_steal_reply(pending);
159 if (dbus_set_error_from_message(&error, reply))
161 qWarning() << "Unable to get volume from pulse audio:" << error.message;
162 dbus_error_free (&error);
164 QTimer::singleShot(0, self, SLOT(connectToPulseAudio()));
166 else if (reply && (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN))
168 const char *object_path;
169 bool argError = dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID);
172 qWarning() << "Unable to get volume from pulse audio:" << error.message;
173 dbus_error_free (&error);
175 QTimer::singleShot(0, self, SLOT(connectToPulseAudio()));
178 qDebug() << __PRETTY_FUNCTION__ << "Object path: " << object_path;
179 self->m_objectPath = object_path;
181 if (self->m_pendingVolumeValue > 0)
183 qDebug() << __PRETTY_FUNCTION__ << "setting volume to level " << self->m_pendingVolumeValue;
184 self->listenVolumeSignals();
185 self->setVolume(self->m_pendingVolumeValue);
186 self->m_pendingVolumeValue = 0;
190 qDebug() << __PRETTY_FUNCTION__ << "getting initial volume level.";
193 dbus_error_init (&error2);
195 msg = dbus_message_new_method_call(0,
197 DBUS_INTERFACE_PROPERTIES,
199 const char* interface = STREAM_RESTORE_IF_ENTRY;
200 const char* property = "Volume";
202 dbus_message_append_args (msg,
203 DBUS_TYPE_STRING, &interface,
204 DBUS_TYPE_STRING, &property,
207 DBusPendingCall *pending = 0;
208 //Return value taken for satisfying coverity tool
209 bool outOfMemory = dbus_connection_send_with_reply( self->m_dbusConnection, msg, &pending, -1 );
210 Q_UNUSED(outOfMemory);
211 self->m_pendingCall = pending;
214 qDebug() << __PRETTY_FUNCTION__ << "pending call sent!";
215 dbus_pending_call_set_notify( pending,
216 (DBusPendingCallNotifyFunction) volumeReply,
219 dbus_message_unref(msg);
223 dbus_message_unref (reply);
226 /********************************************************************
227 * MafwGstRendererVolume::volumeReply
228 ********************************************************************/
229 void MafwGstRendererVolume::volumeReply(DBusPendingCall *pending, MafwGstRendererVolume *self)
231 self->m_pendingCall = 0;
233 qDebug() << __PRETTY_FUNCTION__;
236 reply = dbus_pending_call_steal_reply(pending);
239 dbus_error_init(&error);
241 if (dbus_set_error_from_message(&error, reply))
243 qWarning() << "MafwGstRendererVolume: Unable to get volume from pulse audio:" << error.message;
244 dbus_error_free (&error);
248 DBusMessageIter iter, array_iterator;
249 dbus_message_iter_init (reply, &iter);
250 if(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT)
252 dbus_message_iter_recurse (&iter, &array_iterator);
255 if (dbus_message_iter_get_arg_type (&array_iterator) == DBUS_TYPE_ARRAY)
257 self->readVolumeFromStruct(&array_iterator);
258 Q_EMIT self->volumeChanged(self->m_currentVolume);
262 qCritical("Unable to get initial volume from Pulse Audio!!");
265 dbus_message_unref (reply);
266 self->listenVolumeSignals();
269 /********************************************************************
270 * MafwGstRendererVolume::listenVolumeSignals
271 ********************************************************************/
272 void MafwGstRendererVolume::listenVolumeSignals()
274 qDebug() << __PRETTY_FUNCTION__;
276 DBusMessage *message = 0;
277 char *signal = (char *) STREAM_RESTORE_VOLUME_SIGNAL;
278 char **emptyarray = { 0 };
280 message = dbus_message_new_method_call (0,
285 dbus_message_append_args (message,
286 DBUS_TYPE_STRING, &signal,
287 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &emptyarray, 0,
290 dbus_connection_send (m_dbusConnection, message, NULL);
291 dbus_connection_flush (m_dbusConnection);
292 dbus_message_unref (message);
295 /********************************************************************
296 * MafwGstRendererVolume::handleIncomingMessages
297 ********************************************************************/
298 void MafwGstRendererVolume::handleIncomingMessages (DBusConnection* conn,
299 DBusMessage* message,
300 MafwGstRendererVolume* self)
302 qDebug(__PRETTY_FUNCTION__);
306 dbus_message_has_member (message, "VolumeUpdated") &&
307 dbus_message_has_interface(message, STREAM_RESTORE_IF_ENTRY) &&
308 dbus_message_has_path(message, self->m_objectPath.toAscii()))
310 qDebug() << "MafwGstRendererVolume: VolumeUpdated signal received.";
312 dbus_error_init(&error);
314 if (dbus_set_error_from_message(&error, message))
316 qWarning() << "Got volume error from pulse audio:" << error.message;
317 dbus_error_free (&error);
320 DBusMessageIter iter;
321 dbus_message_iter_init (message, &iter);
323 if (self->readVolumeFromStruct(&iter))
325 Q_EMIT self->volumeChanged(self->m_currentVolume);
328 //When a connection is disconnected, you are guaranteed to get a signal "Disconnected" from the interface DBUS_INTERFACE_LOCAL, path DBUS_PATH_LOCAL.
330 dbus_message_has_member (message, "Disconnected") &&
331 dbus_message_get_interface(message) == QString(DBUS_INTERFACE_LOCAL) &&
332 dbus_message_get_path(message) == QString(DBUS_PATH_LOCAL))
334 qWarning("MafwGstRendererVolume: Connection with pulse audio is disconnected!");
335 QTimer::singleShot(PULSE_RESTART_DELAY, self, SLOT(connectToPulseAudio()));
336 qDebug("MafwGstRendererVolume: Trying to reconnect.");
341 /********************************************************************
342 * There is a dbus variant containing array of structures. Each structure
343 * contains channels postion and the volume level of this position
344 ********************************************************************/
345 bool MafwGstRendererVolume::readVolumeFromStruct(DBusMessageIter *iterator)
347 qDebug(__PRETTY_FUNCTION__);
348 bool volumeChanged = false;
349 DBusMessageIter struct_iterator;
350 dbus_message_iter_recurse (iterator, &struct_iterator);
353 while (dbus_message_iter_get_arg_type(&struct_iterator) == DBUS_TYPE_STRUCT)
355 DBusMessageIter variant;
356 dbus_message_iter_recurse (&struct_iterator, &variant);
357 int channel, value = 0;
359 if (dbus_message_iter_get_arg_type (&variant) == DBUS_TYPE_UINT32)
361 dbus_message_iter_get_basic (&variant, &channel);
362 qDebug("Channel %d", channel);
363 dbus_message_iter_next (&variant);
364 dbus_message_iter_get_basic (&variant, &value);
366 value = qRound((float)value / (float)VOLUME_NORM * 100);
368 //We're interested in all the channels that stream-restore happens to give you.
369 //We have to somehow map between a single volume value and per-channel volume.
370 //The usual way to do the mapping is just to use the loudest channel as
374 uint newVolume = value;
375 if (newVolume != m_currentVolume)
377 m_currentVolume = newVolume;
378 volumeChanged = true;
379 qDebug("MafwGstRendererVolume: current volume level has changed: %d", m_currentVolume);
383 if (m_currentVolume > 100) //MAFW volume has range 0-100
385 qWarning("MafwGstRendererVolume: Pulse audio signals volume level which is out-of range!");
386 m_currentVolume = 100;
392 qWarning("MafwGstRendererVolume: Invalid volume value from pulse audio!");
394 dbus_message_iter_next (&struct_iterator);
396 return volumeChanged;
399 /********************************************************************
400 * MafwGstRendererVolume::getVolume
401 ********************************************************************/
402 uint MafwGstRendererVolume::getVolume()
404 qDebug(__PRETTY_FUNCTION__);
405 return m_currentVolume;
408 /********************************************************************
409 * Incoming value has range 0-99 pulseaudio uses values 0-VOLUME_NORM
410 ********************************************************************/
411 bool MafwGstRendererVolume::setVolume (uint value)
414 qDebug("MafwGstRendererVolume::setVolume (uint %d)", value);
415 if (m_objectPath.isEmpty())
417 qDebug() << "MafwGstRendererVolume: Can not set volume yet. Waiting for RestoreEntry object path";
418 m_pendingVolumeValue = value;
422 const char* interface = STREAM_RESTORE_IF_ENTRY;
423 const char* property = "Volume";
424 if (value > 100) //MAFW volume has range 0-100
426 qWarning("MafwGstRendererVolume: Trying to set volume level which is out-of range!");
429 uint nativeValue = (((float)value / (float)100) * VOLUME_NORM);
430 DBusMessage* message;
431 message = dbus_message_new_method_call(0,
432 m_objectPath.toAscii(),
433 DBUS_INTERFACE_PROPERTIES,
436 if ( dbus_message_append_args(message,
437 DBUS_TYPE_STRING, &interface,
438 DBUS_TYPE_STRING, &property,
441 //Compose the dbus variant containing an array of struct of pair
442 //of unsigned integers va(uu)
443 DBusMessageIter argument_iterator, variant_iterator, array_iterator, struct_iterator;
444 dbus_message_iter_init_append (message, &argument_iterator);
445 dbus_message_iter_open_container (&argument_iterator,
449 dbus_message_iter_open_container (&variant_iterator,
453 dbus_message_iter_open_container (&array_iterator,
458 dbus_message_iter_append_basic (&struct_iterator, DBUS_TYPE_UINT32, &VOLUME_CHANNEL_MONO);
459 dbus_message_iter_append_basic (&struct_iterator, DBUS_TYPE_UINT32, &nativeValue);
461 dbus_message_iter_close_container (&array_iterator, &struct_iterator);
462 dbus_message_iter_close_container (&variant_iterator, &array_iterator);
463 dbus_message_iter_close_container (&argument_iterator, &variant_iterator);
465 dbus_connection_send (m_dbusConnection, message, NULL);
466 dbus_connection_flush (m_dbusConnection);
470 qWarning("Cannot set volume!");
473 dbus_message_unref (message);