Commit b96cbbc8 authored by Jean-Francois Dockes's avatar Jean-Francois Dockes
Browse files

Moderate the load and event rate generated by sound volume changes

parent d2e21fbc
AC_INIT([upmpdcli], [0.1], [jfd@lesbonscomptes.com],
AC_INIT([upmpdcli], [0.2], [jfd@lesbonscomptes.com],
[upmpdcli], [http://www.lesbonscomptes.com/upnpp])
AC_PREREQ([2.53])
AC_CONFIG_SRCDIR([libupnpp/upnpplib.hxx])
......
......@@ -269,8 +269,8 @@ void UpnpDevice::notifyEvent(const string& serviceId,
const vector<string>& names,
const vector<string>& values)
{
LOGDEB("UpnpDevice::notifyEvent: "
<< (names.empty()?"Empty names??":names[0]) << endl);
LOGDEB("UpnpDevice::notifyEvent " << serviceId << " " <<
(names.empty()?"Empty names??":names[0]) << endl);
if (names.empty())
return;
vector<const char *> cnames, cvalues;
......@@ -308,8 +308,8 @@ int timespec_diffms(const struct timespec& old, const struct timespec& recent)
void UpnpDevice::eventloop()
{
int count = 0;
const int loopwait_ms = 500; // Polling mpd every 1/2 S
const int nloopstofull = 10; // Full state every 5 S
const int loopwait_ms = 1000; // Polling mpd every 1 S
const int nloopstofull = 10; // Full state every 10 S
struct timespec wkuptime, earlytime;
bool didearly = false;
......
......@@ -27,6 +27,7 @@
#include <mpd/player.h>
#include <mpd/queue.h>
#include "libupnpp/log.hxx"
#include "mpdcli.hxx"
using namespace std;
......@@ -58,7 +59,7 @@ bool MPDCli::openconn()
}
m_conn = mpd_connection_new(m_host.c_str(), m_port, 0);
if (m_conn == NULL) {
cerr << "mpd_connection_new failed. No memory?" << endl;
LOGERR("mpd_connection_new failed. No memory?" << endl);
return false;
}
......@@ -69,7 +70,7 @@ bool MPDCli::openconn()
if(!m_password.empty()) {
if (!mpd_run_password(M_CONN, m_password.c_str())) {
cerr << "Password wrong" << endl;
LOGERR("Password wrong" << endl);
return false;
}
}
......@@ -80,18 +81,20 @@ bool MPDCli::openconn()
bool MPDCli::showError(const string& who)
{
if (!ok()) {
cerr << "MPDCli::showError: bad state" << endl;
LOGERR("MPDCli::showError: bad state" << endl);
return false;
}
int error = mpd_connection_get_error(M_CONN);
if (error == MPD_ERROR_SUCCESS)
return false;
cerr << who << " failed: " << mpd_connection_get_error_message(M_CONN);
LOGERR(who << " failed: " << mpd_connection_get_error_message(M_CONN)
<< endl);
if (error == MPD_ERROR_SERVER) {
cerr << " server error: " << mpd_connection_get_server_error(M_CONN) ;
LOGERR(who << " server error: " <<
mpd_connection_get_server_error(M_CONN) << endl);
}
cerr << endl;
if (error == MPD_ERROR_CLOSED)
if (openconn())
return true;
......@@ -110,7 +113,7 @@ bool MPDCli::showError(const string& who)
bool MPDCli::updStatus()
{
if (!ok()) {
cerr << "MPDCli::updStatus: bad state" << endl;
LOGERR("MPDCli::updStatus: bad state" << endl);
return false;
}
......@@ -119,7 +122,7 @@ bool MPDCli::updStatus()
if (mpds == 0) {
openconn();
mpds = mpd_run_status(M_CONN);
cerr << "MPDCli::updStatus: can't get status" << endl;
LOGERR("MPDCli::updStatus: can't get status" << endl);
return false;
}
......@@ -162,7 +165,7 @@ bool MPDCli::updStatus()
bool MPDCli::updSong(unordered_map<string, string>& tsong, int pos)
{
// cerr << "MPDCli::updSong" << endl;
// LOGDEB("MPDCli::updSong" << endl);
tsong.clear();
if (!ok())
return false;
......@@ -175,7 +178,7 @@ bool MPDCli::updSong(unordered_map<string, string>& tsong, int pos)
}
if (song == 0) {
cerr << "mpd_run_current_song failed" << endl;
LOGERR("mpd_run_current_song failed" << endl);
return false;
}
......@@ -209,25 +212,28 @@ bool MPDCli::updSong(unordered_map<string, string>& tsong, int pos)
return true;
}
bool MPDCli::setVolume(int volume, bool relative)
bool MPDCli::setVolume(int volume, bool isMute)
{
if (!ok()) {
return false;
}
// Can't set volume if not active
// MPD does not want to set the volume if not active.
if (!(m_stat.state== MpdStatus::MPDS_PLAY) &&
!(m_stat.state == MpdStatus::MPDS_PAUSE)) {
cerr << "MPDCli::setVolume: not active" << endl;
LOGINF("MPDCli::setVolume: not active" << endl);
return true;
}
cerr << "setVolume: vol " << volume << " relative " << relative << endl;
if (volume == 0) {
if (relative) {
LOGDEB("MPDCli::setVolume: vol " << volume << " isMute " << isMute << endl);
if (isMute) {
if (volume) {
// Restore premute volume
m_stat.volume = m_premutevolume;
cerr << "Restoring premute" << endl;
LOGDEB("MPDCli::setVolume: restoring premute " << m_premutevolume
<< endl);
volume = m_stat.volume = m_premutevolume;
} else {
updStatus();
if (m_stat.volume != 0) {
cerr << "Saving premute: " << m_stat.volume << endl;
m_premutevolume = m_stat.volume;
......@@ -235,9 +241,6 @@ bool MPDCli::setVolume(int volume, bool relative)
}
}
if (relative)
volume += m_stat.volume;
if (volume < 0)
volume = 0;
else if (volume > 100)
......@@ -250,14 +253,12 @@ bool MPDCli::setVolume(int volume, bool relative)
int MPDCli::getVolume()
{
if (!updStatus())
return -1;
return m_stat.volume == -1 ? 0: m_stat.volume;
}
bool MPDCli::togglePause()
{
cerr << "MPDCli::togglePause" << endl;
LOGDEB("MPDCli::togglePause" << endl);
if (!ok())
return false;
RETRY_CMD(mpd_run_toggle_pause(M_CONN));
......@@ -266,7 +267,7 @@ bool MPDCli::togglePause()
bool MPDCli::play(int pos)
{
cerr << "MPDCli::play(pos=" << pos << ")" << endl;
LOGDEB("MPDCli::play(pos=" << pos << ")" << endl);
if (!ok())
return false;
if (pos >= 0) {
......@@ -274,11 +275,11 @@ bool MPDCli::play(int pos)
} else {
RETRY_CMD(mpd_run_play(M_CONN));
}
return true;
return updStatus();
}
bool MPDCli::stop()
{
cerr << "MPDCli::stop" << endl;
LOGDEB("MPDCli::stop" << endl);
if (!ok())
return false;
RETRY_CMD(mpd_run_stop(M_CONN));
......@@ -288,14 +289,14 @@ bool MPDCli::seek(int seconds)
{
if (!updStatus())
return -1;
cerr << "MPDCli::seek: pos:"<<m_stat.songpos<<" seconds: "<< seconds<<endl;
LOGDEB("MPDCli::seek: pos:"<<m_stat.songpos<<" seconds: "<< seconds<<endl);
RETRY_CMD(mpd_run_seek_pos(M_CONN, m_stat.songpos, (unsigned int)seconds));
return true;
}
bool MPDCli::next()
{
cerr << "MPDCli::next" << endl;
LOGDEB("MPDCli::next" << endl);
if (!ok())
return false;
RETRY_CMD(mpd_run_next(M_CONN));
......@@ -303,7 +304,7 @@ bool MPDCli::next()
}
bool MPDCli::previous()
{
cerr << "MPDCli::previous" << endl;
LOGDEB("MPDCli::previous" << endl);
if (!ok())
return false;
RETRY_CMD(mpd_run_previous(M_CONN));
......@@ -311,7 +312,7 @@ bool MPDCli::previous()
}
bool MPDCli::repeat(bool on)
{
cerr << "MPDCli::repeat:" << on << endl;
LOGDEB("MPDCli::repeat:" << on << endl);
if (!ok())
return false;
RETRY_CMD(mpd_run_repeat(M_CONN, on));
......@@ -319,7 +320,7 @@ bool MPDCli::repeat(bool on)
}
bool MPDCli::random(bool on)
{
cerr << "MPDCli::random:" << on << endl;
LOGDEB("MPDCli::random:" << on << endl);
if (!ok())
return false;
RETRY_CMD(mpd_run_random(M_CONN, on));
......@@ -327,7 +328,7 @@ bool MPDCli::random(bool on)
}
bool MPDCli::single(bool on)
{
cerr << "MPDCli::single:" << on << endl;
LOGDEB("MPDCli::single:" << on << endl);
if (!ok())
return false;
RETRY_CMD(mpd_run_single(M_CONN, on));
......@@ -336,7 +337,7 @@ bool MPDCli::single(bool on)
int MPDCli::insert(const string& uri, int pos)
{
cerr << "MPDCli::insert at :" << pos << " uri " << uri << endl;
LOGDEB("MPDCli::insert at :" << pos << " uri " << uri << endl);
if (!ok())
return -1;
......@@ -353,7 +354,7 @@ int MPDCli::insert(const string& uri, int pos)
}
bool MPDCli::deleteId(int id)
{
cerr << "MPDCli::deleteId " << id << endl;
LOGDEB("MPDCli::deleteId " << id << endl);
if (!ok())
return -1;
......@@ -362,7 +363,7 @@ bool MPDCli::deleteId(int id)
}
bool MPDCli::statId(int id)
{
cerr << "MPDCli::statId " << id << endl;
LOGDEB("MPDCli::statId " << id << endl);
if (!ok())
return -1;
......@@ -377,7 +378,7 @@ int MPDCli::curpos()
{
if (!updStatus())
return -1;
cerr << "MPDCli::curpos: pos: " << m_stat.songpos << " id "
<< m_stat.songid << endl;
LOGDEB("MPDCli::curpos: pos: " << m_stat.songpos << " id "
<< m_stat.songid << endl);
return m_stat.songpos;
}
......@@ -52,7 +52,7 @@ public:
const std::string& pass="");
~MPDCli();
bool ok() {return m_ok && m_conn;}
bool setVolume(int ivol, bool relative = false);
bool setVolume(int ivol, bool isMute = false);
int getVolume();
bool togglePause();
bool play(int pos = -1);
......
......@@ -72,32 +72,30 @@ public:
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;
}
// Desired volume target. We may delay executing small volume
// changes to avoid saturating with small requests.
int m_desiredvolume;
// Translate MPD state to AVTransport state variables.
bool tpstateMToU(unordered_map<string, string>& state);
};
static const string serviceIdRender("urn:upnp-org:serviceId:RenderingControl");
......@@ -106,7 +104,7 @@ static const string serviceIdTransport("urn:upnp-org:serviceId:AVTransport");
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 @@ UpMpd::UpMpd(const string& deviceid,
}
}
// 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 @@ bool UpMpd::getEventData(bool all, const string& serviceid,
// <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;
//LOGDEB("UpMpd::getEventDataRendering. desiredvolume " <<
// m_desiredvolume << (all?" all " : "") << endl);
if (m_desiredvolume >= 0) {
m_mpdcli->setVolume(m_desiredvolume);
m_desiredvolume = -1;
}
names.push_back("LastChange");
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 @@ int UpMpd::setMute(const SoapArgs& sc, SoapData& data)
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 @@ int UpMpd::setVolume(const SoapArgs& sc, SoapData& data, bool isDb)
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::setVolume(const SoapArgs& sc, SoapData& data, bool isDb)
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 @@ int UpMpd::selectPreset(const SoapArgs& sc, SoapData& data)
// 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::setAVTransportURI(const SoapArgs& sc, SoapData& data, bool setnext)
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::getPositionInfo(const SoapArgs& sc, SoapData& data)
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) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment