openMSX
Mixer.cc
Go to the documentation of this file.
1 #include "Mixer.hh"
2 #include "MSXMixer.hh"
3 #include "NullSoundDriver.hh"
4 #include "SDLSoundDriver.hh"
5 #include "CommandController.hh"
6 #include "CliComm.hh"
7 #include "MSXException.hh"
8 #include "stl.hh"
9 #include "unreachable.hh"
10 #include "build-info.hh"
11 #include <cassert>
12 #include <memory>
13 
14 namespace openmsx {
15 
16 #if defined(_WIN32)
17 static const int defaultsamples = 2048;
18 #elif PLATFORM_ANDROID
19 static const int defaultsamples = 2560;
20 #else
21 static const int defaultsamples = 1024;
22 #endif
23 
24 static EnumSetting<Mixer::SoundDriverType>::Map getSoundDriverMap()
25 {
27  { "null", Mixer::SND_NULL },
28  { "sdl", Mixer::SND_SDL } };
29  return soundDriverMap;
30 }
31 
32 Mixer::Mixer(Reactor& reactor_, CommandController& commandController_)
33  : reactor(reactor_)
34  , commandController(commandController_)
35  , soundDriverSetting(
36  commandController, "sound_driver",
37  "select the sound output driver",
38  Mixer::SND_SDL, getSoundDriverMap())
39  , muteSetting(
40  commandController, "mute",
41  "(un)mute the emulation sound", false, Setting::DONT_SAVE)
42  , masterVolume(
43  commandController, "master_volume",
44  "master volume", 75, 0, 100)
45  , frequencySetting(
46  commandController, "frequency",
47  "mixer frequency", 44100, 11025, 48000)
48  , samplesSetting(
49  commandController, "samples",
50  "mixer samples", defaultsamples, 64, 8192)
51  , muteCount(0)
52 {
53  muteSetting .attach(*this);
54  frequencySetting .attach(*this);
55  samplesSetting .attach(*this);
56  soundDriverSetting.attach(*this);
57 
58  // Set correct initial mute state.
59  if (muteSetting.getBoolean()) ++muteCount;
60 
61  reloadDriver();
62 }
63 
65 {
66  assert(msxMixers.empty());
67  driver.reset();
68 
69  soundDriverSetting.detach(*this);
70  samplesSetting .detach(*this);
71  frequencySetting .detach(*this);
72  muteSetting .detach(*this);
73 }
74 
75 void Mixer::reloadDriver()
76 {
77  // Destroy old driver before attempting to create a new one. Though
78  // this means we end up without driver if creating the new one failed
79  // for some reason.
80 
81  driver = std::make_unique<NullSoundDriver>();
82 
83  try {
84  switch (soundDriverSetting.getEnum()) {
85  case SND_NULL:
86  driver = std::make_unique<NullSoundDriver>();
87  break;
88  case SND_SDL:
89  driver = std::make_unique<SDLSoundDriver>(
90  reactor,
91  frequencySetting.getInt(),
92  samplesSetting.getInt());
93  break;
94  default:
96  }
97  } catch (MSXException& e) {
98  commandController.getCliComm().printWarning(e.getMessage());
99  }
100 
101  muteHelper();
102 }
103 
105 {
106  assert(!contains(msxMixers, &mixer));
107  msxMixers.push_back(&mixer);
108  muteHelper();
109 }
110 
112 {
113  move_pop_back(msxMixers, rfind_unguarded(msxMixers, &mixer));
114  muteHelper();
115 }
116 
117 
119 {
120  if (muteCount++ == 0) {
121  muteHelper();
122  }
123 }
124 
126 {
127  assert(muteCount);
128  if (--muteCount == 0) {
129  muteHelper();
130  }
131 }
132 
133 void Mixer::muteHelper()
134 {
135  bool isMuted = muteCount || msxMixers.empty();
136  unsigned samples = isMuted ? 0 : driver->getSamples();
137  unsigned frequency = driver->getFrequency();
138  for (auto& m : msxMixers) {
139  m->setMixerParams(samples, frequency);
140  }
141 
142  if (isMuted) {
143  driver->mute();
144  } else {
145  driver->unmute();
146  }
147 }
148 
149 void Mixer::uploadBuffer(MSXMixer& /*msxMixer*/, float* buffer, unsigned len)
150 {
151  // can only handle one MSXMixer ATM
152  assert(!msxMixers.empty());
153 
154  driver->uploadBuffer(buffer, len);
155 }
156 
157 void Mixer::update(const Setting& setting)
158 {
159  if (&setting == &muteSetting) {
160  if (muteSetting.getBoolean()) {
161  mute();
162  } else {
163  unmute();
164  }
165  } else if ((&setting == &samplesSetting) ||
166  (&setting == &soundDriverSetting) ||
167  (&setting == &frequencySetting)) {
168  reloadDriver();
169  } else {
170  UNREACHABLE;
171  }
172 }
173 
174 } // namespace openmsx
T getEnum() const noexcept
Definition: EnumSetting.hh:92
Contains the main loop of openMSX.
Definition: Reactor.hh:66
const std::string & getMessage() const &
Definition: MSXException.hh:23
bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition: stl.hh:106
void uploadBuffer(MSXMixer &msxMixer, float *buffer, unsigned len)
Upload new sample data.
Definition: Mixer.cc:149
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:191
Mixer(Reactor &reactor, CommandController &commandController)
Definition: Mixer.cc:32
void attach(Observer< T > &observer)
Definition: Subject.hh:50
bool getBoolean() const noexcept
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void mute()
This methods (un)mute the sound.
Definition: Mixer.cc:118
void unregisterMixer(MSXMixer &mixer)
Unregister per-machine mixer.
Definition: Mixer.cc:111
virtual CliComm & getCliComm()=0
int getInt() const noexcept
void detach(Observer< T > &observer)
Definition: Subject.hh:56
void registerMixer(MSXMixer &mixer)
Register per-machine mixer.
Definition: Mixer.cc:104
auto rfind_unguarded(RANGE &range, const VAL &val)
Similar to the find(_if)_unguarded functions above, but searches from the back to front...
Definition: stl.hh:166
void printWarning(string_view message)
Definition: CliComm.cc:20
void unmute()
Definition: Mixer.cc:125
std::vector< std::pair< std::string, T > > Map
Definition: EnumSetting.hh:35
#define UNREACHABLE
Definition: unreachable.hh:38