devicewidget: Add a latency offset spinbutton

This change adds the ability to change the latency offset of a port with
pavucontrol.
This commit is contained in:
poljar (Damir Jelić) 2012-07-26 15:42:11 +02:00 committed by Tanu Kaskinen
parent 8af6520c60
commit d03f9ffac2
4 changed files with 177 additions and 21 deletions

View File

@ -32,13 +32,16 @@
/*** DeviceWidget ***/ /*** DeviceWidget ***/
DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) : DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) :
MinimalStreamWidget(cobject, x) { MinimalStreamWidget(cobject, x),
offsetButtonEnabled(false) {
x->get_widget("lockToggleButton", lockToggleButton); x->get_widget("lockToggleButton", lockToggleButton);
x->get_widget("muteToggleButton", muteToggleButton); x->get_widget("muteToggleButton", muteToggleButton);
x->get_widget("defaultToggleButton", defaultToggleButton); x->get_widget("defaultToggleButton", defaultToggleButton);
x->get_widget("portSelect", portSelect); x->get_widget("portSelect", portSelect);
x->get_widget("portList", portList); x->get_widget("portList", portList);
x->get_widget("offsetSelect", offsetSelect);
x->get_widget("offsetButton", offsetButton);
this->signal_button_press_event().connect(sigc::mem_fun(*this, &DeviceWidget::onContextTriggerEvent)); this->signal_button_press_event().connect(sigc::mem_fun(*this, &DeviceWidget::onContextTriggerEvent));
muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onMuteToggleButton)); muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onMuteToggleButton));
@ -54,9 +57,13 @@ DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Buil
portList->pack_start(portModel.desc); portList->pack_start(portModel.desc);
portList->signal_changed().connect(sigc::mem_fun(*this, &DeviceWidget::onPortChange)); portList->signal_changed().connect(sigc::mem_fun(*this, &DeviceWidget::onPortChange));
offsetButton->signal_value_changed().connect(sigc::mem_fun(*this, &DeviceWidget::onOffsetChange));
for (unsigned i = 0; i < PA_CHANNELS_MAX; i++) for (unsigned i = 0; i < PA_CHANNELS_MAX; i++)
channelWidgets[i] = NULL; channelWidgets[i] = NULL;
offsetAdjustment = Gtk::Adjustment::create(0.0, -2000.0, 2000.0, 10.0, 50.0, 0.0);
offsetButton->configure(offsetAdjustment, 0, 2);
} }
void DeviceWidget::init(MainWindow* mainWindow, Glib::ustring deviceType) { void DeviceWidget::init(MainWindow* mainWindow, Glib::ustring deviceType) {
@ -120,6 +127,27 @@ void DeviceWidget::onMuteToggleButton() {
void DeviceWidget::onDefaultToggleButton() { void DeviceWidget::onDefaultToggleButton() {
} }
void DeviceWidget::onOffsetChange() {
pa_operation *o;
int64_t offset;
std::ostringstream card_stream;
Glib::ustring card_name;
if (!offsetButtonEnabled)
return;
offset = offsetButton->get_value() * 1000.0;
card_stream << card_index;
card_name = card_stream.str();
if (!(o = pa_context_set_port_latency_offset(get_context(),
card_name.c_str(), activePort.c_str(), offset, NULL, NULL))) {
show_error(_("pa_context_set_port_latency_offset() failed"));
return;
}
pa_operation_unref(o);
}
void DeviceWidget::setDefault(bool isDefault) { void DeviceWidget::setDefault(bool isDefault) {
defaultToggleButton->set_active(isDefault); defaultToggleButton->set_active(isDefault);
/*defaultToggleButton->set_sensitive(!isDefault);*/ /*defaultToggleButton->set_sensitive(!isDefault);*/
@ -133,6 +161,12 @@ bool DeviceWidget::timeoutEvent() {
void DeviceWidget::executeVolumeUpdate() { void DeviceWidget::executeVolumeUpdate() {
} }
void DeviceWidget::setLatencyOffset(int64_t offset) {
offsetButtonEnabled = false;
offsetButton->set_value(offset / 1000.0);
offsetButtonEnabled = true;
}
void DeviceWidget::setBaseVolume(pa_volume_t v) { void DeviceWidget::setBaseVolume(pa_volume_t v) {
for (int i = 0; i < channelMap.channels; i++) for (int i = 0; i < channelMap.channels; i++)
@ -157,10 +191,18 @@ void DeviceWidget::prepareMenu() {
if (active_idx >= 0) if (active_idx >= 0)
portList->set_active(active_idx); portList->set_active(active_idx);
if (ports.size() > 0) if (ports.size() > 0) {
portSelect->show(); portSelect->show();
else
if (pa_context_get_server_protocol_version(get_context()) >= 27)
offsetSelect->show();
else
offsetSelect->hide();
} else {
portSelect->hide(); portSelect->hide();
offsetSelect->hide();
}
} }
bool DeviceWidget::onContextTriggerEvent(GdkEventButton* event) { bool DeviceWidget::onContextTriggerEvent(GdkEventButton* event) {

View File

@ -42,6 +42,9 @@ public:
uint32_t index, card_index; uint32_t index, card_index;
Gtk::ToggleButton *lockToggleButton, *muteToggleButton, *defaultToggleButton; Gtk::ToggleButton *lockToggleButton, *muteToggleButton, *defaultToggleButton;
Gtk::SpinButton *offsetButton;
bool offsetButtonEnabled;
pa_channel_map channelMap; pa_channel_map channelMap;
pa_cvolume volume; pa_cvolume volume;
@ -52,6 +55,8 @@ public:
virtual void onDefaultToggleButton(); virtual void onDefaultToggleButton();
virtual void setDefault(bool isDefault); virtual void setDefault(bool isDefault);
virtual bool onContextTriggerEvent(GdkEventButton*); virtual bool onContextTriggerEvent(GdkEventButton*);
virtual void setLatencyOffset(int64_t offset);
void onOffsetChange();
sigc::connection timeoutConnection; sigc::connection timeoutConnection;
@ -75,7 +80,6 @@ protected:
Gtk::Menu contextMenu; Gtk::Menu contextMenu;
Gtk::MenuItem rename; Gtk::MenuItem rename;
/* Tree model columns */ /* Tree model columns */
class ModelColumns : public Gtk::TreeModel::ColumnRecord class ModelColumns : public Gtk::TreeModel::ColumnRecord
{ {
@ -90,9 +94,10 @@ protected:
ModelColumns portModel; ModelColumns portModel;
Gtk::HBox *portSelect; Gtk::HBox *portSelect, *offsetSelect;
Gtk::ComboBox *portList; Gtk::ComboBox *portList;
Glib::RefPtr<Gtk::ListStore> treeModel; Glib::RefPtr<Gtk::ListStore> treeModel;
Glib::RefPtr<Gtk::Adjustment> offsetAdjustment;
private: private:
Glib::ustring mDeviceType; Glib::ustring mDeviceType;

View File

@ -252,6 +252,17 @@ static void set_icon_name_fallback(Gtk::Image *i, const char *name, Gtk::IconSiz
} }
} }
static void updatePorts(DeviceWidget *w, std::map<Glib::ustring, PortInfo> &ports) {
std::map<Glib::ustring, PortInfo>::iterator it;
it = ports.find(w->activePort);
if (it != ports.end()) {
PortInfo &activePort = it->second;
w->setLatencyOffset(activePort.latency_offset);
}
}
void MainWindow::updateCard(const pa_card_info &info) { void MainWindow::updateCard(const pa_card_info &info) {
CardWidget *w; CardWidget *w;
bool is_new = false; bool is_new = false;
@ -304,6 +315,37 @@ void MainWindow::updateCard(const pa_card_info &info) {
w->ports[p.name] = p; w->ports[p.name] = p;
} }
/* Because the port info for sinks and sources is discontinued we need
* to update the port info for them here. */
if (w->hasSinks) {
std::map<uint32_t, SinkWidget*>::iterator it;
for (it = sinkWidgets.begin() ; it != sinkWidgets.end(); it++) {
SinkWidget *sw = (*it).second;
if (sw->card_index == w->index) {
sw->updating = true;
updatePorts(sw, w->ports);
sw->updating = false;
}
}
}
if (w->hasSources) {
std::map<uint32_t, SourceWidget*>::iterator it;
for (it = sourceWidgets.begin() ; it != sourceWidgets.end(); it++) {
SourceWidget *sw = (*it).second;
if (sw->card_index == w->index) {
sw->updating = true;
updatePorts(sw, w->ports);
sw->updating = false;
}
}
}
w->updating = false; w->updating = false;
w->prepareMenu(); w->prepareMenu();
@ -316,6 +358,7 @@ 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;
std::map<uint32_t, CardWidget*>::iterator cw;
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))
@ -362,6 +405,11 @@ bool 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 : "";
cw = cardWidgets.find(info.card);
if (cw != cardWidgets.end())
updatePorts(w, cw->second->ports);
#ifdef PA_SINK_SET_FORMATS #ifdef PA_SINK_SET_FORMATS
w->setDigital(info.flags & PA_SINK_SET_FORMATS); w->setDigital(info.flags & PA_SINK_SET_FORMATS);
#endif #endif
@ -463,6 +511,7 @@ void MainWindow::updateSource(const pa_source_info &info) {
SourceWidget *w; SourceWidget *w;
bool is_new = false; bool is_new = false;
const char *icon; const char *icon;
std::map<uint32_t, CardWidget*>::iterator cw;
std::set<pa_source_port_info,source_port_prio_compare> port_priorities; std::set<pa_source_port_info,source_port_prio_compare> port_priorities;
if (sourceWidgets.count(info.index)) if (sourceWidgets.count(info.index))
@ -511,6 +560,11 @@ void MainWindow::updateSource(const pa_source_info &info) {
w->activePort = info.active_port ? info.active_port->name : ""; w->activePort = info.active_port ? info.active_port->name : "";
cw = cardWidgets.find(info.card);
if (cw != cardWidgets.end())
updatePorts(w, cw->second->ports);
w->updating = false; w->updating = false;
w->prepareMenu(); w->prepareMenu();

View File

@ -277,11 +277,11 @@
<property name="spacing">3</property> <property name="spacing">3</property>
<child> <child>
<object class="GtkToggleButton" id="muteToggleButton"> <object class="GtkToggleButton" id="muteToggleButton">
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Mute audio</property> <property name="tooltip_text" translatable="yes">Mute audio</property>
<property name="use_action_appearance">False</property>
<property name="relief">none</property> <property name="relief">none</property>
<child> <child>
<object class="GtkImage" id="image20"> <object class="GtkImage" id="image20">
@ -300,11 +300,11 @@
</child> </child>
<child> <child>
<object class="GtkToggleButton" id="lockToggleButton"> <object class="GtkToggleButton" id="lockToggleButton">
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Lock channels together</property> <property name="tooltip_text" translatable="yes">Lock channels together</property>
<property name="use_action_appearance">False</property>
<property name="relief">none</property> <property name="relief">none</property>
<property name="active">True</property> <property name="active">True</property>
<child> <child>
@ -324,11 +324,11 @@
</child> </child>
<child> <child>
<object class="GtkToggleButton" id="defaultToggleButton"> <object class="GtkToggleButton" id="defaultToggleButton">
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Set as fallback</property> <property name="tooltip_text" translatable="yes">Set as fallback</property>
<property name="use_action_appearance">False</property>
<child> <child>
<object class="GtkImage" id="image2"> <object class="GtkImage" id="image2">
<property name="visible">True</property> <property name="visible">True</property>
@ -394,19 +394,77 @@
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkHBox" id="offsetSelect">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Latency offset:&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="offsetButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</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>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">ms</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child> <child>
<object class="GtkTable" id="encodingSelect"> <object class="GtkTable" id="encodingSelect">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="n_rows">2</property> <property name="n_rows">2</property>
<property name="n_columns">3</property> <property name="n_columns">3</property>
<child>
<placeholder/>
</child>
<child> <child>
<object class="GtkCheckButton" id="encodingFormatPCM"> <object class="GtkCheckButton" id="encodingFormatPCM">
<property name="label" translatable="yes">PCM</property> <property name="label" translatable="yes">PCM</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="sensitive">False</property> <property name="sensitive">False</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="active">True</property> <property name="active">True</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
@ -414,10 +472,10 @@
<child> <child>
<object class="GtkCheckButton" id="encodingFormatAC3"> <object class="GtkCheckButton" id="encodingFormatAC3">
<property name="label" translatable="yes">AC3</property> <property name="label" translatable="yes">AC3</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -428,10 +486,10 @@
<child> <child>
<object class="GtkCheckButton" id="encodingFormatDTS"> <object class="GtkCheckButton" id="encodingFormatDTS">
<property name="label" translatable="yes">DTS</property> <property name="label" translatable="yes">DTS</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -442,10 +500,10 @@
<child> <child>
<object class="GtkCheckButton" id="encodingFormatEAC3"> <object class="GtkCheckButton" id="encodingFormatEAC3">
<property name="label" translatable="yes">EAC3</property> <property name="label" translatable="yes">EAC3</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -456,10 +514,10 @@
<child> <child>
<object class="GtkCheckButton" id="encodingFormatMPEG"> <object class="GtkCheckButton" id="encodingFormatMPEG">
<property name="label" translatable="yes">MPEG</property> <property name="label" translatable="yes">MPEG</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -469,14 +527,11 @@
<property name="bottom_attach">2</property> <property name="bottom_attach">2</property>
</packing> </packing>
</child> </child>
<child>
<placeholder/>
</child>
</object> </object>
<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>
<child> <child>
@ -494,7 +549,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">3</property> <property name="position">4</property>
</packing> </packing>
</child> </child>
</object> </object>
@ -1316,10 +1371,10 @@
<child> <child>
<object class="GtkButton" id="deviceButton"> <object class="GtkButton" id="deviceButton">
<property name="label" translatable="yes">Device</property> <property name="label" translatable="yes">Device</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="relief">half</property> <property name="relief">half</property>
<property name="xalign">0</property> <property name="xalign">0</property>
</object> </object>
@ -1344,11 +1399,11 @@
<property name="spacing">3</property> <property name="spacing">3</property>
<child> <child>
<object class="GtkToggleButton" id="muteToggleButton"> <object class="GtkToggleButton" id="muteToggleButton">
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Mute audio</property> <property name="tooltip_text" translatable="yes">Mute audio</property>
<property name="use_action_appearance">False</property>
<property name="relief">none</property> <property name="relief">none</property>
<child> <child>
<object class="GtkImage" id="image20"> <object class="GtkImage" id="image20">
@ -1367,11 +1422,11 @@
</child> </child>
<child> <child>
<object class="GtkToggleButton" id="lockToggleButton"> <object class="GtkToggleButton" id="lockToggleButton">
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Lock channels together</property> <property name="tooltip_text" translatable="yes">Lock channels together</property>
<property name="use_action_appearance">False</property>
<property name="relief">none</property> <property name="relief">none</property>
<property name="active">True</property> <property name="active">True</property>
<child> <child>