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