Switch to side-by-side view

--- a/libupnpp/control/renderingcontrol.cxx
+++ b/libupnpp/control/renderingcontrol.cxx
@@ -25,6 +25,7 @@
 #include <string>                       // for string, operator<<, etc
 #include <utility>                      // for pair
 
+#include "libupnpp/control/description.hxx"
 #include "libupnpp/control/avlastchg.hxx"  // for decodeAVLastChange
 #include "libupnpp/control/service.hxx"  // for VarEventReporter, Service
 #include "libupnpp/log.hxx"             // for LOGERR, LOGDEB1, LOGINF
@@ -47,6 +48,39 @@
     return !SType.compare(0, sz, st, 0, sz);
 }
 
+RenderingControl::RenderingControl(const UPnPDeviceDesc& device,
+                                   const UPnPServiceDesc& service)
+    : Service(device, service), m_volmin(0), m_volmax(100), m_volstep(1)
+{
+    UPnPServiceDesc::Parsed sdesc;
+    if (service.fetchAndParseDesc(device.URLBase, sdesc)) {
+        auto it = sdesc.stateTable.find("Volume");
+        if (it != sdesc.stateTable.end() && it->second.hasValueRange) {
+            setVolParams(it->second.minimum, it->second.maximum,
+                         it->second.step);
+        }
+    }
+    registerCallback();
+}
+
+int RenderingControl::devVolTo0100(int dev_vol)
+{
+    int volume;
+    if (dev_vol < m_volmin)
+        dev_vol = m_volmin;
+    if (dev_vol > m_volmax)
+        dev_vol = m_volmax;
+    if (m_volmin != 0 || m_volmax != 100) {
+        float fact = float(m_volmax - m_volmin) / 100.0;
+        if (fact <= 0.0) // ??
+            fact = 1.0;
+        volume = int((dev_vol - m_volmin) / fact);
+    } else {
+        volume = dev_vol;
+    }
+    return volume;
+}
+
 void RenderingControl::evtCallback(
     const std::unordered_map<std::string, std::string>& props)
 {
@@ -63,9 +97,9 @@
                 LOGDEB1("    " << it1->first << " -> " << 
                         it1->second << endl);
                 if (!it1->first.compare("Volume")) {
+                    int vol = devVolTo0100(atoi(it1->second.c_str()));
                     if (m_reporter) {
-                        m_reporter->changed(it1->first.c_str(),
-                                            atoi(it1->second.c_str()));
+                        m_reporter->changed(it1->first.c_str(), vol);
                     }
                 } else if (!it1->first.compare("Mute")) {
                     bool mute;
@@ -85,8 +119,22 @@
     Service::registerCallback(bind(&RenderingControl::evtCallback, this, _1));
 }
 
-int RenderingControl::setVolume(int volume, const string& channel)
+int RenderingControl::setVolume(int ivol, const string& channel)
 {
+    // Input is always 0-100. Translate to actual range
+    if (ivol < 0)
+        ivol = 0;
+    if (ivol > 100)
+        ivol = 100;
+    int volume = ivol;
+    if (m_volmin != 0 || m_volmax != 100) {
+        float fact = float(m_volmax - m_volmin) / 100.0;
+        volume = m_volmin + int(float(ivol) * fact);
+    }
+    //LOGINF("RenderingControl::setVolume: ivol " << ivol << 
+    //      " m_volmin " << m_volmin << " m_volmax " << m_volmax <<
+    //      " m_volstep " << m_volstep << " volume " << volume << endl);
+
     SoapEncodeInput args(m_serviceType, "SetVolume");
     args("InstanceID", "0")("Channel", channel)
         ("DesiredVolume", SoapHelp::i2s(volume));
@@ -103,14 +151,17 @@
     if (ret != UPNP_E_SUCCESS) {
         return ret;
     }
-    int volume;
-    if (!data.getInt("CurrentVolume", &volume)) {
+    int dev_volume;
+    if (!data.getInt("CurrentVolume", &dev_volume)) {
         LOGERR("RenderingControl:getVolume: missing CurrentVolume in response" 
         << endl);
         return UPNP_E_BAD_RESPONSE;
     }
-    return volume;
+
+    // Output is always 0-100. Translate from device range
+    return devVolTo0100(dev_volume);
 }
+
 int RenderingControl::setMute(bool mute, const string& channel)
 {
     SoapEncodeInput args(m_serviceType, "SetMute");