33 #include "emmintrin.h"
45 , motherBoard(motherBoard_)
46 , commandController(motherBoard.getMSXCommandController())
47 , masterVolume(mixer.getMasterVolume())
48 , speedManager(globalSettings.getSpeedManager())
49 , throttleManager(globalSettings.getThrottleManager())
50 , prevTime(getCurrentTime(), 44100)
51 , soundDeviceInfo(commandController.getMachineInfoCommand())
53 , synchronousCounter(0)
55 hostSampleRate = 44100;
63 masterVolume.
attach(*
this);
64 speedManager.
attach(*
this);
65 throttleManager.
attach(*
this);
73 assert(infos.empty());
75 throttleManager.
detach(*
this);
76 speedManager.
detach(*
this);
77 masterVolume.
detach(*
this);
83 int balance,
unsigned numChannels)
86 const string& name = device.
getName();
88 info.device = &device;
89 info.defaultVolume = volume;
90 info.volumeSetting = std::make_unique<IntegerSetting>(
91 commandController,
tmpStrCat(name,
"_volume"),
92 "the volume of this sound chip", 75, 0, 100);
93 info.balanceSetting = std::make_unique<IntegerSetting>(
94 commandController,
tmpStrCat(name,
"_balance"),
95 "the balance of this sound chip", balance, -100, 100);
97 info.volumeSetting->attach(*
this);
98 info.balanceSetting->attach(*
this);
100 for (
auto i :
xrange(numChannels)) {
102 auto ch_name =
tmpStrCat(name,
"_ch", i + 1);
104 channelSettings.
recordSetting = std::make_unique<StringSetting>(
105 commandController,
tmpStrCat(ch_name,
"_record"),
106 "filename to record this channel to",
110 channelSettings.
muteSetting = std::make_unique<BooleanSetting>(
111 commandController,
tmpStrCat(ch_name,
"_mute"),
112 "sets mute-status of individual sound channels",
116 info.channelSettings.push_back(std::move(channelSettings));
120 auto& i = infos.emplace_back(std::move(info));
121 updateVolumeParams(i);
129 [&](
const SoundDeviceInfo& i) {
return i.device == &device; });
130 it->volumeSetting->detach(*
this);
131 it->balanceSetting->detach(*
this);
132 for (
auto& s : it->channelSettings) {
133 s.recordSetting->detach(*
this);
134 s.muteSetting->detach(*
this);
144 ++synchronousCounter;
145 if (synchronousCounter == 1) {
149 assert(synchronousCounter > 0);
150 --synchronousCounter;
151 if (synchronousCounter == 0) {
159 return synchronousCounter ? 1.0 : speedManager.
getSpeed();
165 float mixBuffer[8192 * 2];
172 assert(
count <= 8192);
175 generate(mixBuffer, time,
count);
177 if (!muteCount && fragmentSize) {
196 static inline void mul(
float* buf,
int n,
float f)
201 assume_SSE_aligned(buf);
213 static inline void mulAcc(
214 float* __restrict acc,
const float* __restrict mul,
int n,
float f)
217 assume_SSE_aligned(acc);
218 assume_SSE_aligned(mul);
221 acc[i + 0] += mul[i + 0] * f;
222 acc[i + 1] += mul[i + 1] * f;
223 acc[i + 2] += mul[i + 2] * f;
224 acc[i + 3] += mul[i + 3] * f;
231 static inline void mulExpand(
float* buf,
int n,
float l,
float r)
237 buf[2 * i + 0] = l *
t;
238 buf[2 * i + 1] = r *
t;
244 static inline void mulExpandAcc(
245 float* __restrict acc,
const float* __restrict mul,
int n,
251 acc[2 * i + 0] += l *
t;
252 acc[2 * i + 1] += r *
t;
258 static inline void mulMix2(
float* buf,
int n,
float l1,
float l2,
float r1,
float r2)
262 auto t1 = buf[2 * i + 0];
263 auto t2 = buf[2 * i + 1];
264 buf[2 * i + 0] = l1 * t1 + l2 * t2;
265 buf[2 * i + 1] = r1 * t1 + r2 * t2;
271 static inline void mulMix2Acc(
272 float* __restrict acc,
const float* __restrict mul,
int n,
273 float l1,
float l2,
float r1,
float r2)
277 auto t1 = mul[2 * i + 0];
278 auto t2 = mul[2 * i + 1];
279 acc[2 * i + 0] += l1 * t1 + l2 * t2;
280 acc[2 * i + 1] += r1 * t1 + r2 * t2;
299 constexpr
auto R = 511.0f / 512.0f;
302 static inline float filterMonoNull(
float t0,
float* __restrict out,
int n)
317 static inline std::tuple<float, float> filterStereoNull(
318 float tl0,
float tr0,
float* __restrict out,
int n)
325 out[2 * i + 0] = tl1 - tl0;
326 out[2 * i + 1] = tr1 - tr0;
330 return std::tuple(tl0, tr0);
334 static inline float filterMonoMono(
float t0,
const float* __restrict in,
335 float* __restrict out,
int n)
340 auto t1 =
R * t0 + in[i];
350 static inline std::tuple<float, float>
351 filterStereoMono(
float tl0,
float tr0,
const float* __restrict in,
352 float* __restrict out,
int n)
358 auto tl1 =
R * tl0 +
x;
359 auto tr1 =
R * tr0 +
x;
360 out[2 * i + 0] = tl1 - tl0;
361 out[2 * i + 1] = tr1 - tr0;
365 return std::tuple(tl0, tr0);
369 static inline std::tuple<float, float>
370 filterStereoStereo(
float tl0,
float tr0,
const float* __restrict in,
371 float* __restrict out,
int n)
376 auto tl1 =
R * tl0 + in[2 * i + 0];
377 auto tr1 =
R * tr0 + in[2 * i + 1];
378 out[2 * i + 0] = tl1 - tl0;
379 out[2 * i + 1] = tr1 - tr0;
383 return std::tuple(tl0, tr0);
387 static inline std::tuple<float, float>
388 filterBothStereo(
float tl0,
float tr0,
const float* __restrict inM,
389 const float* __restrict inS,
float* __restrict out,
int n)
395 auto tl1 =
R * tl0 + inS[2 * i + 0] + m;
396 auto tr1 =
R * tr0 + inS[2 * i + 1] + m;
397 out[2 * i + 0] = tl1 - tl0;
398 out[2 * i + 1] = tr1 - tr0;
402 return std::tuple(tl0, tr0);
405 static bool approxEqual(
float x,
float y)
407 constexpr
float threshold = 1.0f / 32768;
408 return std::abs(
x - y) < threshold;
411 void MSXMixer::generate(
float* output, EmuTime::param time,
unsigned samples)
424 for (
auto& info : infos) {
425 bool ignore = info.device->updateBuffer(0, dummyBuf, time);
437 constexpr
unsigned HAS_MONO_FLAG = 1;
438 constexpr
unsigned HAS_STEREO_FLAG = 2;
439 unsigned usedBuffers = 0;
443 for (
auto& info : infos) {
444 SoundDevice& device = *info.device;
445 auto l1 = info.left1;
446 auto r1 = info.right1;
447 if (!device.isStereo()) {
449 if (!(usedBuffers & HAS_MONO_FLAG)) {
450 if (device.updateBuffer(samples, monoBuf, time)) {
451 usedBuffers |= HAS_MONO_FLAG;
452 mul(monoBuf, samples, l1);
455 if (device.updateBuffer(samples, tmpBuf, time)) {
456 mulAcc(monoBuf, tmpBuf, samples, l1);
460 if (!(usedBuffers & HAS_STEREO_FLAG)) {
461 if (device.updateBuffer(samples, stereoBuf, time)) {
462 usedBuffers |= HAS_STEREO_FLAG;
463 mulExpand(stereoBuf, samples, l1, r1);
466 if (device.updateBuffer(samples, tmpBuf, time)) {
467 mulExpandAcc(stereoBuf, tmpBuf, samples, l1, r1);
472 auto l2 = info.left2;
473 auto r2 = info.right2;
477 if (!(usedBuffers & HAS_STEREO_FLAG)) {
478 if (device.updateBuffer(samples, stereoBuf, time)) {
479 usedBuffers |= HAS_STEREO_FLAG;
480 mul(stereoBuf, 2 * samples, l1);
483 if (device.updateBuffer(samples, tmpBuf, time)) {
484 mulAcc(stereoBuf, tmpBuf, 2 * samples, l1);
488 if (!(usedBuffers & HAS_STEREO_FLAG)) {
489 if (device.updateBuffer(samples, stereoBuf, time)) {
490 usedBuffers |= HAS_STEREO_FLAG;
491 mulMix2(stereoBuf, samples, l1, l2, r1, r2);
494 if (device.updateBuffer(samples, tmpBuf, time)) {
495 mulMix2Acc(stereoBuf, tmpBuf, samples, l1, l2, r1, r2);
503 switch (usedBuffers) {
505 if (approxEqual(tl0, tr0)) {
506 if (approxEqual(tl0, 0.0f)) {
509 memset(output, 0, 2 * samples *
sizeof(
float));
513 tl0 = filterMonoNull(tl0, output, samples);
517 std::tie(tl0, tr0) = filterStereoNull(tl0, tr0, output, samples);
522 if (approxEqual(tl0, tr0)) {
524 tl0 = filterMonoMono(tl0, monoBuf, output, samples);
528 std::tie(tl0, tr0) = filterStereoMono(tl0, tr0, monoBuf, output, samples);
532 case HAS_STEREO_FLAG:
533 std::tie(tl0, tr0) = filterStereoStereo(tl0, tr0, stereoBuf, output, samples);
537 std::tie(tl0, tr0) = filterBothStereo(tl0, tr0, monoBuf, stereoBuf, output, samples);
544 return info.device->isStereo() ||
545 info.balanceSetting->getInt() != 0;
551 if (muteCount == 0) {
560 if (muteCount == 0) {
572 void MSXMixer::reschedule()
577 void MSXMixer::reschedule2()
579 unsigned size = (!muteCount && fragmentSize) ? fragmentSize : 512;
587 hostSampleRate = newSampleRate;
588 fragmentSize = newFragmentSize;
592 for (
auto& info : infos) {
593 info.device->setOutputRate(newSampleRate);
599 if ((recorder !=
nullptr) != (newRecorder !=
nullptr)) {
602 recorder = newRecorder;
605 void MSXMixer::update(
const Setting& setting) noexcept
607 if (&setting == &masterVolume) {
608 updateMasterVolume();
609 }
else if (
dynamic_cast<const IntegerSetting*
>(&setting)) {
611 [&](
const SoundDeviceInfo& i) {
612 return &setting ==
one_of(i.volumeSetting .get(),
613 i.balanceSetting.get()); });
614 updateVolumeParams(*it);
615 }
else if (
dynamic_cast<const StringSetting*
>(&setting)) {
616 changeRecordSetting(setting);
617 }
else if (
dynamic_cast<const BooleanSetting*
>(&setting)) {
618 changeMuteSetting(setting);
624 void MSXMixer::changeRecordSetting(
const Setting& setting)
626 for (
auto& info : infos) {
627 unsigned channel = 0;
628 for (
auto& s : info.channelSettings) {
629 if (s.recordSetting.get() == &setting) {
630 info.device->recordChannel(
633 s.recordSetting->getString()))));
642 void MSXMixer::changeMuteSetting(
const Setting& setting)
644 for (
auto& info : infos) {
645 unsigned channel = 0;
646 for (
auto& s : info.channelSettings) {
647 if (s.muteSetting.get() == &setting) {
648 info.device->muteChannel(
649 channel, s.muteSetting->getBoolean());
658 void MSXMixer::update(
const SpeedManager& ) noexcept
660 if (synchronousCounter == 0) {
661 setMixerParams(fragmentSize, hostSampleRate);
672 void MSXMixer::update(
const ThrottleManager& ) noexcept
678 void MSXMixer::updateVolumeParams(SoundDeviceInfo& info)
680 int mVolume = masterVolume.
getInt();
681 int dVolume = info.volumeSetting->getInt();
682 float volume = info.defaultVolume * mVolume * dVolume / (100.0f * 100.0f);
683 int balance = info.balanceSetting->getInt();
684 auto [l1, r1, l2, r2] = [&] {
685 if (info.device->isStereo()) {
687 float b = (balance + 100.0f) / 100.0f;
691 volume * sqrtf(
std::max(0.0f, 1.0f - b)),
695 float b = balance / 100.0f;
697 volume * sqrtf(
std::max(0.0f, 1.0f - b)),
706 float b = (balance + 100.0f) / 200.0f;
708 volume * sqrtf(
std::max(0.0f, 1.0f - b)),
715 auto [ampL, ampR] = info.device->getAmplificationFactor();
716 info.left1 = l1 * ampL;
717 info.right1 = r1 * ampR;
718 info.left2 = l2 * ampL;
719 info.right2 = r2 * ampR;
722 void MSXMixer::updateMasterVolume()
724 for (
auto& p : infos) {
725 updateVolumeParams(p);
732 [&](
auto& i) {
return i.device == &device; });
733 updateVolumeParams(*it);
736 void MSXMixer::executeUntil(EmuTime::param time)
757 return i.device->getName() == name;
759 return (it !=
end(infos)) ? it->device :
nullptr;
762 MSXMixer::SoundDeviceInfoTopic::SoundDeviceInfoTopic(
764 :
InfoTopic(machineInfoCommand,
"sounddevice")
768 void MSXMixer::SoundDeviceInfoTopic::execute(
771 auto& msxMixer =
OUTER(MSXMixer, soundDeviceInfo);
772 switch (tokens.
size()) {
776 [](
auto& info) { return info.device->getName(); }));
779 SoundDevice* device = msxMixer.findDevice(tokens[2].getString());
781 throw CommandException(
"Unknown sound device");
783 result = device->getDescription();
787 throw CommandException(
"Too many parameters");
791 string MSXMixer::SoundDeviceInfoTopic::help(
const vector<string>& )
const
793 return "Shows a list of available sound devices.\n";
796 void MSXMixer::SoundDeviceInfoTopic::tabCompletion(vector<string>& tokens)
const
798 if (tokens.size() == 3) {
800 OUTER(MSXMixer, soundDeviceInfo).infos,
801 [](
auto& info) {
return info.device->getName(); }));
802 completeString(tokens, devices);
void addWave(unsigned num, float *data)
virtual void update(UpdateType type, std::string_view name, std::string_view value)=0
EmuTime getFastAdd(unsigned n) const
unsigned getTicksTill(EmuTime::param e) const
Calculate the number of ticks for this clock until the given time.
void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
void setFreq(unsigned freq)
Change the frequency at which this clock ticks.
This class contains settings that are used by several other class (including some singletons).
int getInt() const noexcept
CliComm & getCliComm() override
void mute()
TODO This methods (un)mute the sound.
void setRecorder(AviRecorder *recorder)
void registerSound(SoundDevice &device, float volume, int balance, unsigned numChannels)
Use this method to register a given sounddevice.
void setSynchronousMode(bool synchronous)
If we're recording, we want to emulate sound at 100% emutime speed.
void updateSoftwareVolume(SoundDevice &device)
Used by SoundDevice::setSoftwareVolume()
unsigned getSampleRate() const
double getEffectiveSpeed() const
Returns the ratio of emutime-speed per realtime-speed.
SoundDevice * findDevice(std::string_view name) const
bool needStereoRecording() const
MSXMixer(Mixer &mixer, MSXMotherBoard &motherBoard, GlobalSettings &globalSettings)
void updateStream(EmuTime::param time)
Use this method to force an 'early' call to all updateBuffer() methods.
void setMixerParams(unsigned fragmentSize, unsigned sampleRate)
Set new fragment size and sample frequency.
void unregisterSound(SoundDevice &device)
Every sounddevice must unregister before it is destructed.
void registerMixer(MSXMixer &mixer)
Register per-machine mixer.
void uploadBuffer(MSXMixer &msxMixer, float *buffer, unsigned len)
Upload new sample data.
void unregisterMixer(MSXMixer &mixer)
Unregister per-machine mixer.
Every class that wants to get scheduled at some point must inherit from this class.
void setSyncPoint(EmuTime::param timestamp)
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
virtual void setOutputRate(unsigned sampleRate)=0
When a SoundDevice registers itself with the Mixer, the Mixer sets the required sampleRate through th...
const std::string & getName() const
Get the unique name that identifies this sound device.
double getSpeed() const
Return the desired ratio between emutime and real time.
void detach(Observer< T > &observer)
void attach(Observer< T > &observer)
constexpr index_type size() const noexcept
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
string expandTilde(string path)
Expand the '~' character to the users home directory.
This file implemented 3 utility functions:
constexpr KeyMatrixPosition x
Keyboard bindings.
bool any_of(InputRange &&range, UnaryPredicate pred)
auto find_if(InputRange &&range, UnaryPredicate pred)
size_t size(std::string_view utf8)
constexpr auto transform(Range &&range, UnaryOp op)
#define OUTER(type, member)
constexpr ITER find_if_unguarded(ITER first, ITER last, PRED pred)
Faster alternative to 'find_if' when it's guaranteed that the predicate will be true for at least one...
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
constexpr auto rfind_if_unguarded(RANGE &range, PRED pred)
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))>>
TemporaryString tmpStrCat(Ts &&... ts)
std::unique_ptr< StringSetting > recordSetting
std::unique_ptr< BooleanSetting > muteSetting
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
constexpr auto xrange(T e)
constexpr auto end(const zstring_view &x)