--- a/upmpd/upmpd.cxx
+++ b/upmpd/upmpd.cxx
@@ -72,32 +72,30 @@
 									   std::vector<std::string>& names, 
 									   std::vector<std::string>& values);
 
-	// Shared
+	// Re-implemented from the base class and shared by both services
     virtual bool getEventData(bool all, const string& serviceid, 
 							  std::vector<std::string>& names, 
 							  std::vector<std::string>& values);
 
 private:
 	MPDCli *m_mpdcli;
+
 	// State variable storage
-	unordered_map<string, string> m_rdupdates;
+	unordered_map<string, string> m_rdstate;
 	unordered_map<string, string> m_tpstate;
+
+	// Translate MPD state to Renderer state variables.
+	bool rdstateMToU(unordered_map<string, string>& state);
+	// Translate MPD state to AVTransport state variables.
+	bool tpstateMToU(unordered_map<string, string>& state);
 
 	// My track identifiers (for cleaning up)
 	set<int> m_songids;
 
-	// The two services use different methods for recording changed
-	// state: The RenderingControl actions record changes one at a
-	// time while the AVTransport recomputes the whole state by
-	// querying MPD and runs a diff with the previous state
-
-	// Record RenderingControl state change
-	void renderingUpdate(const string& nm, const string& val) {
-		m_rdupdates[nm] = val;
-	}
-
-	// Translate MPD state to AVTransport state variables.
-	bool tpstateMToU(unordered_map<string, string>& state);
+	// Desired volume target. We may delay executing small volume
+	// changes to avoid saturating with small requests.
+	int m_desiredvolume;
+
 };
 
 static const string serviceIdRender("urn:upnp-org:serviceId:RenderingControl");
@@ -106,7 +104,7 @@
 UpMpd::UpMpd(const string& deviceid, 
 			 const unordered_map<string, string>& xmlfiles,
 			 MPDCli *mpdcli)
-	: UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli)
+	: UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_desiredvolume(-1)
 {
 	addServiceType(serviceIdRender,
 				   "urn:schemas-upnp-org:service:RenderingControl:1");
@@ -188,10 +186,10 @@
 	}
 }
 
-// This is called at regular intervals by the polling loop to retrieve
-// changed state variables for each of the services (the list of
-// services was defined in the base class by the "addServiceTypes()"
-// calls during construction). 
+// This is called by the polling loop at regular intervals, or when
+// triggered, to retrieve changed state variables for each of the
+// services (the list of services was defined in the base class by the
+// "addServiceTypes()" calls during construction).
 //
 // We might add a method for triggering an event from the action
 // methods after changing state, which would really act only if the
@@ -228,41 +226,68 @@
 //     <VolumeDB channel=���Master��� val=���24���/>
 //   </InstanceID>
 // </Event>
+
+bool UpMpd::rdstateMToU(unordered_map<string, string>& status)
+{
+	const struct MpdStatus &mpds = m_mpdcli->getStatus();
+
+	int volume = m_desiredvolume >= 0 ? m_desiredvolume : mpds.volume;
+	if (volume < 0)
+		volume = 0;
+	char cvalue[30];
+	sprintf(cvalue, "%d", volume);
+	status["Volume"] = cvalue;
+	sprintf(cvalue, "%d", percentodbvalue(volume));
+	status["VolumeDB"] =  cvalue;
+	status["Mute"] =  volume == 0 ? "1" : "0";
+}
+
 bool UpMpd::getEventDataRendering(bool all, std::vector<std::string>& names, 
 								  std::vector<std::string>& values)
 {
-	if (all) {
-		// Record all values in the changes structure.
-		int volume = m_mpdcli->getVolume();
-		renderingUpdate("Mute", volume == 0 ? "1" : "0");
-		char cvalue[40];
-		sprintf(cvalue, "%d", volume);
-		renderingUpdate("Volume", cvalue);
-		sprintf(cvalue, "%d", percentodbvalue(volume));
-		renderingUpdate("VolumeDB", cvalue);
-		renderingUpdate("PresetNameList", "FactoryDefault");
-	}
-
-	if (m_rdupdates.empty())
-		return true;
-
-	names.push_back("LastChange");
+	//LOGDEB("UpMpd::getEventDataRendering. desiredvolume " << 
+	//		   m_desiredvolume << (all?" all " : "") << endl);
+	if (m_desiredvolume >= 0) {
+		m_mpdcli->setVolume(m_desiredvolume);
+		m_desiredvolume = -1;
+	}
+
+	unordered_map<string, string> newstate;
+	rdstateMToU(newstate);
+	if (all)
+		m_rdstate.clear();
 
 	string 
 		chgdata("<Event xmlns=\"urn:schemas-upnp-org:metadata-1-0/AVT_RCS\">\n"
 				"<InstanceID val=\"0\">\n");
-	for (unordered_map<string, string>::const_iterator it = m_rdupdates.begin();
-		 it != m_rdupdates.end(); it++) {
+
+	bool changefound = false;
+	for (unordered_map<string, string>::const_iterator it = newstate.begin();
+		 it != newstate.end(); it++) {
+
+		const string& oldvalue = mapget(m_rdstate, it->first);
+		if (!it->second.compare(oldvalue))
+			continue;
+
+		changefound = true;
+
 		chgdata += "<";
 		chgdata += it->first;
-		chgdata += " channel=\"Master\" val=\"";
+		chgdata += " val=\"";
 		chgdata += xmlquote(it->second);
 		chgdata += "\"/>\n";
 	}
 	chgdata += "</InstanceID>\n</Event>\n";
 
+	if (!changefound) {
+		return true;
+	}
+
+	names.push_back("LastChange");
 	values.push_back(chgdata);
-	m_rdupdates.clear();
+
+	m_rdstate = newstate;
+
 	return true;
 }
 
@@ -310,12 +335,14 @@
 		return UPNP_E_INVALID_PARAM;
 	}
 	if (it->second[0] == 'F' || it->second[0] == '0') {
-		// Relative set of 0 -> restore pre-mute
-		m_mpdcli->setVolume(0, 1);
-		renderingUpdate("Mute", "0");
+		// Restore pre-mute
+		m_mpdcli->setVolume(1, true);
 	} else if (it->second[0] == 'T' || it->second[0] == '1') {
-		m_mpdcli->setVolume(0);
-		renderingUpdate("Mute", "1");
+		if (m_desiredvolume >= 0) {
+			m_mpdcli->setVolume(m_desiredvolume);
+			m_desiredvolume = -1;
+		}
+		m_mpdcli->setVolume(0, true);
 	} else {
 		return UPNP_E_INVALID_PARAM;
 	}
@@ -356,13 +383,18 @@
 	if (volume < 0 || volume > 100) {
 		return UPNP_E_INVALID_PARAM;
 	}
-	m_mpdcli->setVolume(volume);
-
-	char cvalue[30];
-	sprintf(cvalue, "%d", volume);
-	renderingUpdate("Volume", cvalue);
-	sprintf(cvalue, "%d", percentodbvalue(volume));
-	renderingUpdate("VolumeDB", cvalue);
+	
+	int previous_volume = m_mpdcli->getVolume();
+	int delta = previous_volume - volume;
+	if (delta < 0)
+		delta = -delta;
+	LOGDEB("UpMpd::setVolume: volume " << volume << " delta " << delta << endl);
+	if (delta >= 5) {
+		m_mpdcli->setVolume(volume);
+		m_desiredvolume = -1;
+	} else {
+		m_desiredvolume = volume;
+	}
 
 	loopWakeup();
 	return UPNP_E_SUCCESS;
@@ -370,6 +402,7 @@
 
 int UpMpd::getVolume(const SoapArgs& sc, SoapData& data, bool isDb)
 {
+	// LOGDEB("UpMpd::getVolume" << endl);
 	map<string, string>::const_iterator it;
 
 	it = sc.args.find("Channel");
@@ -409,11 +442,6 @@
 	// Well there is only the volume actually...
 	int volume = 50;
 	m_mpdcli->setVolume(volume);
-	char cvalue[30];
-	sprintf(cvalue, "%d", volume);
-	renderingUpdate("Volume", cvalue);
-	sprintf(cvalue, "%d", percentodbvalue(volume));
-	renderingUpdate("VolumeDB", cvalue);
 
 	return UPNP_E_SUCCESS;
 }
@@ -661,7 +689,7 @@
 int UpMpd::getPositionInfo(const SoapArgs& sc, SoapData& data)
 {
 	const struct MpdStatus &mpds = m_mpdcli->getStatus();
-	LOGDEB("UpMpd::getPositionInfo. State: " << mpds.state << endl);
+	//LOGDEB("UpMpd::getPositionInfo. State: " << mpds.state << endl);
 
 	bool is_song = (mpds.state == MpdStatus::MPDS_PLAY) || 
 		(mpds.state == MpdStatus::MPDS_PAUSE);
@@ -710,7 +738,7 @@
 int UpMpd::getTransportInfo(const SoapArgs& sc, SoapData& data)
 {
 	const struct MpdStatus &mpds = m_mpdcli->getStatus();
-	LOGDEB("UpMpd::getTransportInfo. State: " << mpds.state << endl);
+	//LOGDEB("UpMpd::getTransportInfo. State: " << mpds.state << endl);
 
 	string tstate("STOPPED");
 	switch(mpds.state) {