From: "poljar (Damir Jeli?)" <poljarinho@xxxxxxxxx> This change adds the ability to change the latency offset of a port with pavucontrol. --- src/devicewidget.cc | 48 ++++++++++++++++++++++++++-- src/devicewidget.h | 9 ++++-- src/mainwindow.cc | 59 ++++++++++++++++++++++++++++++++++ src/pavucontrol.glade | 87 +++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 182 insertions(+), 21 deletions(-) diff --git a/src/devicewidget.cc b/src/devicewidget.cc index dda1763..ba08cc8 100644 --- a/src/devicewidget.cc +++ b/src/devicewidget.cc @@ -32,13 +32,16 @@ /*** DeviceWidget ***/ 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("muteToggleButton", muteToggleButton); x->get_widget("defaultToggleButton", defaultToggleButton); x->get_widget("portSelect", portSelect); 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)); 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->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++) 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) { @@ -120,6 +127,27 @@ void DeviceWidget::onMuteToggleButton() { 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) { defaultToggleButton->set_active(isDefault); /*defaultToggleButton->set_sensitive(!isDefault);*/ @@ -133,6 +161,12 @@ bool DeviceWidget::timeoutEvent() { 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) { for (int i = 0; i < channelMap.channels; i++) @@ -157,10 +191,18 @@ void DeviceWidget::prepareMenu() { if (active_idx >= 0) portList->set_active(active_idx); - if (ports.size() > 0) + if (ports.size() > 0) { portSelect->show(); - else + + if (pa_context_get_server_protocol_version(get_context()) >= 27) + offsetSelect->show(); + else + offsetSelect->hide(); + + } else { portSelect->hide(); + offsetSelect->hide(); + } } bool DeviceWidget::onContextTriggerEvent(GdkEventButton* event) { diff --git a/src/devicewidget.h b/src/devicewidget.h index 94f14e6..f099e6e 100644 --- a/src/devicewidget.h +++ b/src/devicewidget.h @@ -42,6 +42,9 @@ public: uint32_t index, card_index; Gtk::ToggleButton *lockToggleButton, *muteToggleButton, *defaultToggleButton; + Gtk::SpinButton *offsetButton; + + bool offsetButtonEnabled; pa_channel_map channelMap; pa_cvolume volume; @@ -52,6 +55,8 @@ public: virtual void onDefaultToggleButton(); virtual void setDefault(bool isDefault); virtual bool onContextTriggerEvent(GdkEventButton*); + virtual void setLatencyOffset(int64_t offset); + void onOffsetChange(); sigc::connection timeoutConnection; @@ -75,7 +80,6 @@ protected: Gtk::Menu contextMenu; Gtk::MenuItem rename; - /* Tree model columns */ class ModelColumns : public Gtk::TreeModel::ColumnRecord { @@ -90,9 +94,10 @@ protected: ModelColumns portModel; - Gtk::HBox *portSelect; + Gtk::HBox *portSelect, *offsetSelect; Gtk::ComboBox *portList; Glib::RefPtr<Gtk::ListStore> treeModel; + Glib::RefPtr<Gtk::Adjustment> offsetAdjustment; private: Glib::ustring mDeviceType; diff --git a/src/mainwindow.cc b/src/mainwindow.cc index fe085ef..b981542 100644 --- a/src/mainwindow.cc +++ b/src/mainwindow.cc @@ -252,6 +252,18 @@ 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; + PortInfo *activePort; + + it = ports.find(w->activePort); + + if (it != ports.end()) { + activePort = &(it->second); + w->setLatencyOffset(activePort->latency_offset); + } +} + void MainWindow::updateCard(const pa_card_info &info) { CardWidget *w; bool is_new = false; @@ -304,6 +316,37 @@ void MainWindow::updateCard(const pa_card_info &info) { 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->prepareMenu(); @@ -316,6 +359,7 @@ bool MainWindow::updateSink(const pa_sink_info &info) { SinkWidget *w; bool is_new = false; const char *icon; + std::map<uint32_t, CardWidget*>::iterator cw; std::set<pa_sink_port_info,sink_port_prio_compare> port_priorities; if (sinkWidgets.count(info.index)) @@ -362,6 +406,13 @@ bool MainWindow::updateSink(const pa_sink_info &info) { w->activePort = info.active_port ? info.active_port->name : ""; + if (info.card) { + cw = cardWidgets.find(info.card); + + if (cw != cardWidgets.end()) + updatePorts(w, cw->second->ports); + } + #ifdef PA_SINK_SET_FORMATS w->setDigital(info.flags & PA_SINK_SET_FORMATS); #endif @@ -463,6 +514,7 @@ void MainWindow::updateSource(const pa_source_info &info) { SourceWidget *w; bool is_new = false; const char *icon; + std::map<uint32_t, CardWidget*>::iterator cw; std::set<pa_source_port_info,source_port_prio_compare> port_priorities; if (sourceWidgets.count(info.index)) @@ -511,6 +563,13 @@ void MainWindow::updateSource(const pa_source_info &info) { w->activePort = info.active_port ? info.active_port->name : ""; + if (info.card) { + cw = cardWidgets.find(info.card); + + if (cw != cardWidgets.end()) + updatePorts(w, cw->second->ports); + } + w->updating = false; w->prepareMenu(); diff --git a/src/pavucontrol.glade b/src/pavucontrol.glade index e569ba6..befdee0 100644 --- a/src/pavucontrol.glade +++ b/src/pavucontrol.glade @@ -277,11 +277,11 @@ <property name="spacing">3</property> <child> <object class="GtkToggleButton" id="muteToggleButton"> + <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="tooltip_text" translatable="yes">Mute audio</property> - <property name="use_action_appearance">False</property> <property name="relief">none</property> <child> <object class="GtkImage" id="image20"> @@ -300,11 +300,11 @@ </child> <child> <object class="GtkToggleButton" id="lockToggleButton"> + <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</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="active">True</property> <child> @@ -324,11 +324,11 @@ </child> <child> <object class="GtkToggleButton" id="defaultToggleButton"> + <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="tooltip_text" translatable="yes">Set as fallback</property> - <property name="use_action_appearance">False</property> <child> <object class="GtkImage" id="image2"> <property name="visible">True</property> @@ -395,18 +395,76 @@ </packing> </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"><b>Latency offset:</b></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> <object class="GtkTable" id="encodingSelect"> <property name="can_focus">False</property> <property name="n_rows">2</property> <property name="n_columns">3</property> <child> + <placeholder/> + </child> + <child> <object class="GtkCheckButton" id="encodingFormatPCM"> <property name="label" translatable="yes">PCM</property> + <property name="use_action_appearance">False</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> @@ -414,10 +472,10 @@ <child> <object class="GtkCheckButton" id="encodingFormatAC3"> <property name="label" translatable="yes">AC3</property> + <property name="use_action_appearance">False</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> @@ -428,10 +486,10 @@ <child> <object class="GtkCheckButton" id="encodingFormatDTS"> <property name="label" translatable="yes">DTS</property> + <property name="use_action_appearance">False</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> @@ -442,10 +500,10 @@ <child> <object class="GtkCheckButton" id="encodingFormatEAC3"> <property name="label" translatable="yes">EAC3</property> + <property name="use_action_appearance">False</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> @@ -456,10 +514,10 @@ <child> <object class="GtkCheckButton" id="encodingFormatMPEG"> <property name="label" translatable="yes">MPEG</property> + <property name="use_action_appearance">False</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> @@ -469,14 +527,11 @@ <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> + <property name="position">3</property> </packing> </child> <child> @@ -494,7 +549,7 @@ <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">3</property> + <property name="position">4</property> </packing> </child> </object> @@ -1316,10 +1371,10 @@ <child> <object class="GtkButton" id="deviceButton"> <property name="label" translatable="yes">Device</property> + <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> - <property name="use_action_appearance">False</property> <property name="relief">half</property> <property name="xalign">0</property> </object> @@ -1344,11 +1399,11 @@ <property name="spacing">3</property> <child> <object class="GtkToggleButton" id="muteToggleButton"> + <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="tooltip_text" translatable="yes">Mute audio</property> - <property name="use_action_appearance">False</property> <property name="relief">none</property> <child> <object class="GtkImage" id="image20"> @@ -1367,11 +1422,11 @@ </child> <child> <object class="GtkToggleButton" id="lockToggleButton"> + <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</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="active">True</property> <child> -- 1.7.11.3