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