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