MythTV  master
volumebase.cpp
Go to the documentation of this file.
1 #include <cstdio>
2 #include <cstdlib>
3 
4 #include <algorithm>
5 
6 #include <QString>
7 #include <QMutex>
8 #include <QMutexLocker>
9 
10 #include "libmythbase/mthread.h"
12 
13 #include "volumebase.h"
14 
15 
16 namespace {
17 class VolumeWriteBackThread : public MThread
18 {
19  VolumeWriteBackThread() : MThread("VolumeWriteBack") { }
20 
21  public:
22  // Singleton
23  static VolumeWriteBackThread *Instance()
24  {
25  QMutexLocker lock(&s_mutex);
26  static auto *s_instance = new VolumeWriteBackThread;
27  return s_instance;
28  }
29 
30  VolumeWriteBackThread(const VolumeWriteBackThread &) = delete;
31  VolumeWriteBackThread & operator =(const VolumeWriteBackThread &) = delete;
32 
33  void SetVolume(int value)
34  {
35  QMutexLocker lock(&m_mutex);
36 
37  if (m_volume == value)
38  return;
39  m_volume = value;
40 
41  switch (m_state)
42  {
43  case kRunning:
44  break;
45  case kFinished:
46  wait();
47  [[fallthrough]];
48  case kStopped:
49  m_state = kRunning;
50  start();
51  break;
52  }
53  }
54 
55  protected:
56  void run(void) override // MThread
57  {
58  m_state = kRunning;
59  RunProlog();
60 
61  static constexpr std::chrono::milliseconds holdoff { 500ms }; // min ms between Db writes
62  QString controlLabel = gCoreContext->GetSetting("MixerControl", "PCM");
63  controlLabel += "MixerVolume";
64 
65  QMutexLocker lock(&m_mutex);
66  while (gCoreContext && !gCoreContext->IsExiting())
67  {
68  int volume = m_volume;
69  lock.unlock();
70 
71  // Update the dbase with the new volume
72  gCoreContext->SaveSetting(controlLabel, volume);
73 
74  // Ignore further volume changes for the holdoff period
76  usleep(holdoff); // cppcheck-suppress usleepCalled
77  setTerminationEnabled(false);
78 
79  lock.relock();
80  if (volume == m_volume)
81  break;
82  }
83 
84  m_state = kFinished;
85  RunEpilog();
86  }
87 
88  private:
89  static QMutex s_mutex;
90  QMutex mutable m_mutex;
91  enum { kStopped, kRunning, kFinished } m_state {kStopped};
92  int m_volume {-1};
93 };
94 
95 QMutex VolumeWriteBackThread::s_mutex;
96 } // namespace
97 
98 
100 {
101  m_internalVol = gCoreContext->GetBoolSetting("MythControlsVolume", true);
103  (gCoreContext->GetSetting("MixerDevice", "default").toLower() == "software");
104 }
105 
106 bool VolumeBase::SWVolume(void) const
107 {
108  return m_swvol;
109 }
110 
111 void VolumeBase::SWVolume(bool set)
112 {
113  if (m_swvolSetting)
114  return;
115  m_swvol = set;
116 }
117 
119 {
120  return m_volume;
121 }
122 
124 {
125  m_volume = std::max(std::min(value, 100), 0);
126  UpdateVolume();
127 
128  // Throttle Db writes
129  VolumeWriteBackThread::Instance()->SetVolume(m_volume);
130 }
131 
133 {
134  SetCurrentVolume(m_volume + change);
135 }
136 
138 {
139  m_currentMuteState = mstate;
140  UpdateVolume();
141  return m_currentMuteState;
142 }
143 
145 {
146  bool is_muted = GetMuteState() == kMuteAll;
147  SetMuteState((is_muted) ? kMuteOff : kMuteAll);
148 }
149 
151 {
152  return m_currentMuteState;
153 }
154 
156 {
157  MuteState next = cur;
158 
159  switch (cur)
160  {
161  case kMuteOff:
162  next = kMuteLeft;
163  break;
164  case kMuteLeft:
165  next = kMuteRight;
166  break;
167  case kMuteRight:
168  next = kMuteAll;
169  break;
170  case kMuteAll:
171  next = kMuteOff;
172  break;
173  }
174 
175  return (next);
176 }
177 
179 {
180  int new_volume = m_volume;
182  {
183  new_volume = 0;
184  }
185 
186  if (m_swvol)
187  {
188  SetSWVolume(new_volume, false);
189  return;
190  }
191 
192  for (int i = 0; i < m_channels; i++)
193  {
194  SetVolumeChannel(i, new_volume);
195  }
196 
197  // Individual channel muting is handled in GetAudioData,
198  // this code demonstrates the old method.
199  // if (m_currentMuteState == kMuteLeft)
200  // {
201  // SetVolumeChannel(0, 0);
202  // }
203  // else if (m_currentMuteState == kMuteRight)
204  // {
205  // SetVolumeChannel(1, 0);
206  // }
207 }
208 
210 {
211  // Read the volume from the audio driver and setup our internal state to match
212  if (m_swvol)
213  m_volume = GetSWVolume();
214  else
216 }
217 
218 void VolumeBase::SetChannels(int new_channels)
219 {
220  m_channels = new_channels;
221 }
VolumeBase::SetCurrentVolume
virtual void SetCurrentVolume(int value)
Definition: volumebase.cpp:123
VolumeBase::GetVolumeChannel
virtual int GetVolumeChannel(int channel) const =0
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
VolumeBase::AdjustCurrentVolume
virtual void AdjustCurrentVolume(int change)
Definition: volumebase.cpp:132
VolumeBase::GetCurrentVolume
virtual uint GetCurrentVolume(void) const
Definition: volumebase.cpp:118
kMuteRight
@ kMuteRight
Definition: volumebase.h:9
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
VolumeBase::GetSWVolume
virtual int GetSWVolume(void)=0
MThread::setTerminationEnabled
static void setTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:330
VolumeBase::m_internalVol
bool m_internalVol
Definition: volumebase.h:41
MThread::usleep
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
VolumeBase::SetMuteState
virtual MuteState SetMuteState(MuteState)
Definition: volumebase.cpp:137
VolumeBase::SyncVolume
void SyncVolume(void)
Definition: volumebase.cpp:209
MThread::RunProlog
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
MuteState
MuteState
Definition: volumebase.h:6
VolumeBase::m_swvolSetting
bool m_swvolSetting
Definition: volumebase.h:48
VolumeBase::ToggleMute
virtual void ToggleMute(void)
Definition: volumebase.cpp:144
VolumeBase::m_volume
int m_volume
Definition: volumebase.h:45
VolumeBase::SetSWVolume
virtual void SetSWVolume(int new_volume, bool save)=0
VolumeBase::GetMuteState
virtual MuteState GetMuteState(void) const
Definition: volumebase.cpp:150
VolumeBase::NextMuteState
static MuteState NextMuteState(MuteState)
Definition: volumebase.cpp:155
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
VolumeBase::VolumeBase
VolumeBase()
Definition: volumebase.cpp:99
kMuteLeft
@ kMuteLeft
Definition: volumebase.h:8
kMuteOff
@ kMuteOff
Definition: volumebase.h:7
uint
unsigned int uint
Definition: compat.h:81
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
VolumeBase::SWVolume
bool SWVolume(void) const
Definition: volumebase.cpp:106
MThread::run
virtual void run(void)
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: mthread.cpp:315
volumebase.h
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:906
VolumeBase::m_channels
int m_channels
Definition: volumebase.h:49
mythcorecontext.h
MThread::operator=
MThread & operator=(const MThread &)=delete
VolumeBase::m_swvol
bool m_swvol
Definition: volumebase.h:47
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
mthread.h
VolumeBase::SetVolumeChannel
virtual void SetVolumeChannel(int channel, int volume)=0
VolumeBase::SetChannels
void SetChannels(int new_channels)
Definition: volumebase.cpp:218
VolumeBase::UpdateVolume
void UpdateVolume(void)
Definition: volumebase.cpp:178
MythCoreContext::IsExiting
bool IsExiting(void)
Definition: mythcorecontext.cpp:2117
kMuteAll
@ kMuteAll
Definition: volumebase.h:10
MythCoreContext::SaveSetting
void SaveSetting(const QString &key, int newValue)
Definition: mythcorecontext.cpp:881
VolumeBase::m_currentMuteState
MuteState m_currentMuteState
Definition: volumebase.h:46
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:898