Add UI to select the formats supported by the receiver attached to a digitial sink

This commit is contained in:
Colin Guthrie 2011-08-09 02:02:39 +02:00
parent 546e679047
commit 479e7bcd6e
7 changed files with 293 additions and 10 deletions

View File

@ -298,10 +298,10 @@ void MainWindow::updateCard(const pa_card_info &info) {
updateDeviceVisibility(); updateDeviceVisibility();
} }
void MainWindow::updateSink(const pa_sink_info &info) { bool MainWindow::updateSink(const pa_sink_info &info) {
SinkWidget *w; SinkWidget *w;
bool is_new = false; bool is_new = false;
const char *icon; const char *icon, *profile;
std::set<pa_sink_port_info,sink_port_prio_compare> port_priorities; std::set<pa_sink_port_info,sink_port_prio_compare> port_priorities;
if (sinkWidgets.count(info.index)) if (sinkWidgets.count(info.index))
@ -348,12 +348,18 @@ void MainWindow::updateSink(const pa_sink_info &info) {
w->activePort = info.active_port ? info.active_port->name : ""; w->activePort = info.active_port ? info.active_port->name : "";
/* Can we do digital? This is a hack just now... we should expose some nice properties to indicate we can do digitial*/
profile = pa_proplist_gets(info.proplist, "device.profile.name");
w->setDigital(profile && 0 == strncmp("iec958", profile, 6));
w->updating = false; w->updating = false;
w->prepareMenu(); w->prepareMenu();
if (is_new) if (is_new)
updateDeviceVisibility(); updateDeviceVisibility();
return is_new;
} }
static void suspended_callback(pa_stream *s, void *userdata) { static void suspended_callback(pa_stream *s, void *userdata) {
@ -760,6 +766,38 @@ void MainWindow::updateRole(const pa_ext_stream_restore_info &info) {
updateDeviceVisibility(); updateDeviceVisibility();
} }
#if HAVE_EXT_DEVICE_RESTORE_API
void MainWindow::updateDeviceInfo(const pa_ext_device_restore_info &info) {
if (sinkWidgets.count(info.index)) {
SinkWidget *w;
pa_format_info *format;
w = sinkWidgets[info.index];
w->updating = true;
/* Unselect everything */
for (int j = 1; j < PAVU_NUM_ENCODINGS; ++j)
w->encodings[j].widget->set_active(false);
for (uint8_t i = 0; i < info.n_formats; ++i) {
format = info.formats[i];
for (int j = 1; j < PAVU_NUM_ENCODINGS; ++j) {
if (format->encoding == w->encodings[j].encoding) {
w->encodings[j].widget->set_active(true);
break;
}
}
}
w->updating = false;
}
}
#endif
void MainWindow::updateVolumeMeter(uint32_t source_index, uint32_t sink_input_idx, double v) { void MainWindow::updateVolumeMeter(uint32_t source_index, uint32_t sink_input_idx, double v) {
if (sink_input_idx != PA_INVALID_INDEX) { if (sink_input_idx != PA_INVALID_INDEX) {

View File

@ -23,7 +23,9 @@
#include "pavucontrol.h" #include "pavucontrol.h"
#include <pulse/ext-stream-restore.h> #include <pulse/ext-stream-restore.h>
#if HAVE_EXT_DEVICE_RESTORE_API
# include <pulse/ext-device-restore.h>
#endif
class CardWidget; class CardWidget;
class SinkWidget; class SinkWidget;
@ -39,7 +41,7 @@ public:
virtual ~MainWindow(); virtual ~MainWindow();
void updateCard(const pa_card_info &info); void updateCard(const pa_card_info &info);
void updateSink(const pa_sink_info &info); bool updateSink(const pa_sink_info &info);
void updateSource(const pa_source_info &info); void updateSource(const pa_source_info &info);
void updateSinkInput(const pa_sink_input_info &info); void updateSinkInput(const pa_sink_input_info &info);
void updateSourceOutput(const pa_source_output_info &info); void updateSourceOutput(const pa_source_output_info &info);
@ -47,6 +49,9 @@ public:
void updateServer(const pa_server_info &info); void updateServer(const pa_server_info &info);
void updateVolumeMeter(uint32_t source_index, uint32_t sink_input_index, double v); void updateVolumeMeter(uint32_t source_index, uint32_t sink_input_index, double v);
void updateRole(const pa_ext_stream_restore_info &info); void updateRole(const pa_ext_stream_restore_info &info);
#if HAVE_EXT_DEVICE_RESTORE_API
void updateDeviceInfo(const pa_ext_device_restore_info &info);
#endif
void removeCard(uint32_t index); void removeCard(uint32_t index);
void removeSink(uint32_t index); void removeSink(uint32_t index);

View File

@ -88,7 +88,11 @@ void card_cb(pa_context *, const pa_card_info *i, int eol, void *userdata) {
w->updateCard(*i); w->updateCard(*i);
} }
void sink_cb(pa_context *, const pa_sink_info *i, int eol, void *userdata) { #if HAVE_EXT_DEVICE_RESTORE_API
static void ext_device_restore_subscribe_cb(pa_context *c, uint32_t idx, void *userdata);
#endif
void sink_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata); MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) { if (eol < 0) {
@ -104,7 +108,12 @@ void sink_cb(pa_context *, const pa_sink_info *i, int eol, void *userdata) {
return; return;
} }
#if HAVE_EXT_DEVICE_RESTORE_API
if (w->updateSink(*i))
ext_device_restore_subscribe_cb(c, i->index, w);
#else
w->updateSink(*i); w->updateSink(*i);
#endif
} }
void source_cb(pa_context *, const pa_source_info *i, int eol, void *userdata) { void source_cb(pa_context *, const pa_source_info *i, int eol, void *userdata) {
@ -251,6 +260,43 @@ static void ext_stream_restore_subscribe_cb(pa_context *c, void *userdata) {
pa_operation_unref(o); pa_operation_unref(o);
} }
#if HAVE_EXT_DEVICE_RESTORE_API
void ext_device_restore_read_cb(
pa_context *,
const pa_ext_device_restore_info *i,
int eol,
void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
dec_outstanding(w);
g_debug(_("Failed to initialize device restore extension: %s"), pa_strerror(pa_context_errno(context)));
return;
}
if (eol > 0) {
dec_outstanding(w);
return;
}
/* Do something with a widget when this part is written */
w->updateDeviceInfo(*i);
}
static void ext_device_restore_subscribe_cb(pa_context *c, uint32_t idx, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
pa_operation *o;
if (!(o = pa_ext_device_restore_read_sink_formats(c, idx, ext_device_restore_read_cb, w))) {
show_error(_("pa_ext_device_restore_read_sink_formats() failed"));
return;
}
pa_operation_unref(o);
}
#endif
void ext_device_manager_read_cb( void ext_device_manager_read_cb(
pa_context *, pa_context *,
const pa_ext_device_manager_info *, const pa_ext_device_manager_info *,
@ -485,6 +531,21 @@ void context_state_callback(pa_context *c, void *userdata) {
} else } else
g_debug(_("Failed to initialize stream_restore extension: %s"), pa_strerror(pa_context_errno(context))); g_debug(_("Failed to initialize stream_restore extension: %s"), pa_strerror(pa_context_errno(context)));
#if HAVE_EXT_DEVICE_RESTORE_API
/* TODO Change this to just the test function */
if ((o = pa_ext_device_restore_read_sink_formats_all(c, ext_device_restore_read_cb, w))) {
pa_operation_unref(o);
n_outstanding++;
pa_ext_device_restore_set_subscribe_cb(c, ext_device_restore_subscribe_cb, w);
if ((o = pa_ext_device_restore_subscribe(c, 1, NULL, NULL)))
pa_operation_unref(o);
} else
g_debug(_("Failed to initialize device restore extension: %s"), pa_strerror(pa_context_errno(context)));
#endif
if ((o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) { if ((o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) {
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding++; n_outstanding++;

View File

@ -392,6 +392,91 @@
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkTable" id="encodingSelect">
<property name="can_focus">False</property>
<property name="n_rows">2</property>
<property name="n_columns">3</property>
<child>
<object class="GtkCheckButton" id="encodingFormatPCM">
<property name="label" translatable="yes">PCM</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object>
</child>
<child>
<object class="GtkCheckButton" id="encodingFormatAC3">
<property name="label" translatable="yes">AC3</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="encodingFormatDTS">
<property name="label" translatable="yes">DTS</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="encodingFormatEAC3">
<property name="label" translatable="yes">EAC3</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="encodingFormatMPEG">
<property name="label" translatable="yes">MPEG</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child> <child>
<object class="GtkVBox" id="channelsVBox"> <object class="GtkVBox" id="channelsVBox">
<property name="visible">True</property> <property name="visible">True</property>
@ -407,7 +492,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">False</property>
<property name="position">2</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
</object> </object>
@ -1069,7 +1154,7 @@
<child> <child>
<object class="GtkLabel" id="connectingLabel"> <object class="GtkLabel" id="connectingLabel">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="no">...</property> <property name="label">...</property>
<property name="use_markup">True</property> <property name="use_markup">True</property>
</object> </object>
<packing> <packing>
@ -1130,6 +1215,10 @@
<property name="invisible_char">●</property> <property name="invisible_char">●</property>
<property name="activates_default">True</property> <property name="activates_default">True</property>
<property name="width_chars">60</property> <property name="width_chars">60</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>

View File

@ -40,6 +40,7 @@
#endif #endif
#define HAVE_SOURCE_OUTPUT_VOLUMES PA_CHECK_VERSION(0,99,0) #define HAVE_SOURCE_OUTPUT_VOLUMES PA_CHECK_VERSION(0,99,0)
#define HAVE_EXT_DEVICE_RESTORE_API PA_CHECK_VERSION(0,99,0)
enum SinkInputType { enum SinkInputType {
SINK_INPUT_ALL, SINK_INPUT_ALL,

View File

@ -22,14 +22,47 @@
#include <config.h> #include <config.h>
#endif #endif
#include <canberra-gtk.h>
#include "sinkwidget.h" #include "sinkwidget.h"
#include <canberra-gtk.h>
#if HAVE_EXT_DEVICE_RESTORE_API
# include <pulse/format.h>
# include <pulse/ext-device-restore.h>
#endif
#include "i18n.h" #include "i18n.h"
SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) : SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) :
DeviceWidget(cobject, x) { DeviceWidget(cobject, x) {
#if HAVE_EXT_DEVICE_RESTORE_API
uint8_t i = 0;
x->get_widget("encodingSelect", encodingSelect);
encodings[i].encoding = PA_ENCODING_PCM;
x->get_widget("encodingFormatPCM", encodings[i].widget);
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
++i;
encodings[i].encoding = PA_ENCODING_AC3_IEC61937;
x->get_widget("encodingFormatAC3", encodings[i].widget);
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
++i;
encodings[i].encoding = PA_ENCODING_EAC3_IEC61937;
x->get_widget("encodingFormatEAC3", encodings[i].widget);
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
++i;
encodings[i].encoding = PA_ENCODING_MPEG_IEC61937;
x->get_widget("encodingFormatMPEG", encodings[i].widget);
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
++i;
encodings[i].encoding = PA_ENCODING_DTS_IEC61937;
x->get_widget("encodingFormatDTS", encodings[i].widget);
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
#endif
} }
SinkWidget* SinkWidget::create(MainWindow* mainWindow) { SinkWidget* SinkWidget::create(MainWindow* mainWindow) {
@ -120,3 +153,42 @@ void SinkWidget::onPortChange() {
} }
} }
} }
void SinkWidget::setDigital(bool digital) {
#if HAVE_EXT_DEVICE_RESTORE_API
if (digital)
encodingSelect->show();
else
encodingSelect->hide();
#endif
}
void SinkWidget::onEncodingsChange() {
#if HAVE_EXT_DEVICE_RESTORE_API
pa_operation* o;
uint8_t n_formats = 0;
pa_format_info **formats;
if (updating)
return;
formats = (pa_format_info**)malloc(sizeof(pa_format_info*) * PAVU_NUM_ENCODINGS);
for (int i = 0; i < PAVU_NUM_ENCODINGS; ++i) {
if (encodings[i].widget->get_active()) {
formats[n_formats] = pa_format_info_new();
formats[n_formats]->encoding = encodings[i].encoding;
++n_formats;
}
}
if (!(o = pa_ext_device_restore_save_sink_formats(get_context(), index, n_formats, formats, NULL, NULL))) {
show_error(_("pa_ext_device_restore_save_sink_formats() failed"));
free(formats);
return;
}
free(formats);
pa_operation_unref(o);
#endif
}

View File

@ -22,9 +22,19 @@
#define sinkwidget_h #define sinkwidget_h
#include "pavucontrol.h" #include "pavucontrol.h"
#include "devicewidget.h" #include "devicewidget.h"
#if HAVE_EXT_DEVICE_RESTORE_API
# include <pulse/format.h>
# define PAVU_NUM_ENCODINGS 5
typedef struct {
pa_encoding encoding;
Gtk::CheckButton *widget;
} encodingList;
#endif
class SinkWidget : public DeviceWidget { class SinkWidget : public DeviceWidget {
public: public:
SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x); SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x);
@ -34,12 +44,19 @@ public:
uint32_t index, monitor_index, card_index; uint32_t index, monitor_index, card_index;
bool can_decibel; bool can_decibel;
#if HAVE_EXT_DEVICE_RESTORE_API
encodingList encodings[PAVU_NUM_ENCODINGS];
Gtk::Table *encodingSelect;
#endif
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
virtual void executeVolumeUpdate(); virtual void executeVolumeUpdate();
virtual void onDefaultToggleButton(); virtual void onDefaultToggleButton();
void setDigital(bool);
protected: protected:
virtual void onPortChange(); virtual void onPortChange();
virtual void onEncodingsChange();
}; };
#endif #endif