Skip to content

Commit 4f4412b

Browse files
authored
nsyshid: Play Emulated Portal Audio via Mono Audio (#1478)
1 parent 00ff554 commit 4f4412b

File tree

9 files changed

+254
-41
lines changed

9 files changed

+254
-41
lines changed

src/Cafe/HW/Latte/Core/LatteShaderCache.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ class BootSoundPlayer
209209

210210
try
211211
{
212-
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(true, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
212+
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::TV, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
213213
if(!bootSndAudioDev)
214214
return;
215215
}

src/Cafe/OS/libs/nsyshid/Skylander.cpp

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "Backend.h"
77

88
#include "Common/FileStream.h"
9+
#include "audio/IAudioAPI.h"
10+
#include "config/CemuConfig.h"
911

1012
namespace nsyshid
1113
{
@@ -558,6 +560,26 @@ namespace nsyshid
558560

559561
Device::WriteResult SkylanderPortalDevice::Write(WriteMessage* message)
560562
{
563+
if (message->length != 64) {
564+
cemu_assert_error();
565+
}
566+
567+
if (!g_portalAudio)
568+
{
569+
// Portal audio is mono channel, 16 bit audio.
570+
// Audio is unsigned 16 bit, supplied as 64 bytes which is 32 samples per block
571+
g_portalAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::Portal, 8000, 32, 16);
572+
}
573+
std::array<sint16, 32> mono_samples;
574+
for (unsigned int i = 0; i < mono_samples.size(); ++i)
575+
{
576+
sint16 sample = static_cast<uint16>(message->data[i * 2 + 1]) << 8 | static_cast<uint16>(message->data[i * 2]);
577+
mono_samples[i] = sample;
578+
}
579+
if (g_portalAudio)
580+
{
581+
g_portalAudio->FeedBlock(mono_samples.data());
582+
}
561583
message->bytesWritten = message->length;
562584
return Device::WriteResult::Success;
563585
}
@@ -604,20 +626,20 @@ namespace nsyshid
604626
*(uint16be*)(currentWritePtr + 7) = 0x001D; // wDescriptorLength
605627
currentWritePtr = currentWritePtr + 9;
606628
// endpoint descriptor 1
607-
*(uint8*)(currentWritePtr + 0) = 7; // bLength
608-
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
609-
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
610-
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
629+
*(uint8*)(currentWritePtr + 0) = 7; // bLength
630+
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
631+
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
632+
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
611633
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
612-
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
634+
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
613635
currentWritePtr = currentWritePtr + 7;
614636
// endpoint descriptor 2
615-
*(uint8*)(currentWritePtr + 0) = 7; // bLength
616-
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
617-
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
618-
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
637+
*(uint8*)(currentWritePtr + 0) = 7; // bLength
638+
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
639+
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
640+
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
619641
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
620-
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
642+
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
621643
currentWritePtr = currentWritePtr + 7;
622644

623645
cemu_assert_debug((currentWritePtr - configurationDescriptor) == 0x29);
@@ -628,8 +650,8 @@ namespace nsyshid
628650
}
629651

630652
bool SkylanderPortalDevice::SetIdle(uint8 ifIndex,
631-
uint8 reportId,
632-
uint8 duration)
653+
uint8 reportId,
654+
uint8 duration)
633655
{
634656
return true;
635657
}

src/Cafe/OS/libs/snd_core/ax_out.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ namespace snd_core
404404
{
405405
try
406406
{
407-
g_tvAudio = IAudioAPI::CreateDeviceFromConfig(true, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
407+
g_tvAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::TV, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
408408
}
409409
catch (std::runtime_error& ex)
410410
{
@@ -417,7 +417,7 @@ namespace snd_core
417417
{
418418
try
419419
{
420-
g_padAudio = IAudioAPI::CreateDeviceFromConfig(false, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
420+
g_padAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::Gamepad, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
421421
if(g_padAudio)
422422
g_padVolume = g_padAudio->GetVolume();
423423
}
@@ -442,6 +442,11 @@ namespace snd_core
442442
g_padAudio->Stop();
443443
g_padAudio.reset();
444444
}
445+
if (g_portalAudio)
446+
{
447+
g_portalAudio->Stop();
448+
g_portalAudio.reset();
449+
}
445450
}
446451

447452
void AXOut_updateDevicePlayState(bool isPlaying)
@@ -462,6 +467,14 @@ namespace snd_core
462467
else
463468
g_padAudio->Stop();
464469
}
470+
471+
if (g_portalAudio)
472+
{
473+
if (isPlaying)
474+
g_portalAudio->Play();
475+
else
476+
g_portalAudio->Stop();
477+
}
465478
}
466479

467480
// called periodically to check for AX updates

src/audio/IAudioAPI.cpp

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
std::shared_mutex g_audioMutex;
1414
AudioAPIPtr g_tvAudio;
1515
AudioAPIPtr g_padAudio;
16+
AudioAPIPtr g_portalAudio;
1617
std::atomic_int32_t g_padVolume = 0;
1718

1819
uint32 IAudioAPI::s_audioDelay = 2;
1920
std::array<bool, IAudioAPI::AudioAPIEnd> IAudioAPI::s_availableApis{};
2021

2122
IAudioAPI::IAudioAPI(uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample)
22-
: m_samplerate(samplerate), m_channels(channels), m_samplesPerBlock(samples_per_block), m_bitsPerSample(bits_per_sample)
23+
: m_samplerate(samplerate), m_channels(channels), m_samplesPerBlock(samples_per_block), m_bitsPerSample(bits_per_sample)
2324
{
2425
m_bytesPerBlock = samples_per_block * channels * (bits_per_sample / 8);
2526
InitWFX(m_samplerate, m_channels, m_bitsPerSample);
@@ -80,7 +81,7 @@ void IAudioAPI::InitializeStatic()
8081
#if BOOST_OS_WINDOWS
8182
s_availableApis[DirectSound] = true;
8283
s_availableApis[XAudio2] = XAudio2API::InitializeStatic();
83-
if(!s_availableApis[XAudio2]) // don't try to initialize the older lib if the newer version is available
84+
if (!s_availableApis[XAudio2]) // don't try to initialize the older lib if the newer version is available
8485
s_availableApis[XAudio27] = XAudio27API::InitializeStatic();
8586
#endif
8687
#if HAS_CUBEB
@@ -97,38 +98,38 @@ bool IAudioAPI::IsAudioAPIAvailable(AudioAPI api)
9798
return false;
9899
}
99100

100-
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample)
101+
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample)
101102
{
102-
auto& config = GetConfig();
103-
sint32 channels = CemuConfig::AudioChannelsToNChannels(TV ? config.tv_channels : config.pad_channels);
104-
return CreateDeviceFromConfig(TV, rate, channels, samples_per_block, bits_per_sample);
103+
sint32 channels = CemuConfig::AudioChannelsToNChannels(AudioTypeToChannels(type));
104+
return CreateDeviceFromConfig(type, rate, channels, samples_per_block, bits_per_sample);
105105
}
106106

107-
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
107+
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
108108
{
109109
AudioAPIPtr audioAPIDev;
110110

111111
auto& config = GetConfig();
112112

113113
const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api;
114-
auto& selectedDevice = TV ? config.tv_device : config.pad_device;
114+
auto selectedDevice = GetDeviceFromType(type);
115115

116-
if(selectedDevice.empty())
116+
if (selectedDevice.empty())
117117
return {};
118118

119119
IAudioAPI::DeviceDescriptionPtr device_description;
120120
if (IAudioAPI::IsAudioAPIAvailable(audio_api))
121121
{
122122
auto devices = IAudioAPI::GetDevices(audio_api);
123-
const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) {return d->GetIdentifier() == selectedDevice; });
123+
const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) { return d->GetIdentifier() == selectedDevice; });
124124
if (it != devices.end())
125125
device_description = *it;
126126
}
127127
if (!device_description)
128128
throw std::runtime_error("failed to find selected device while trying to create audio device");
129129

130130
audioAPIDev = CreateDevice(audio_api, device_description, rate, channels, samples_per_block, bits_per_sample);
131-
audioAPIDev->SetVolume(TV ? config.tv_volume : config.pad_volume);
131+
audioAPIDev->SetVolume(GetVolumeFromType(type));
132+
132133
return audioAPIDev;
133134
}
134135

@@ -137,7 +138,7 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
137138
if (!IsAudioAPIAvailable(api))
138139
return {};
139140

140-
switch(api)
141+
switch (api)
141142
{
142143
#if BOOST_OS_WINDOWS
143144
case DirectSound:
@@ -157,11 +158,11 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
157158
}
158159
#endif
159160
#if HAS_CUBEB
160-
case Cubeb:
161-
{
162-
const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device);
163-
return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
164-
}
161+
case Cubeb:
162+
{
163+
const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device);
164+
return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
165+
}
165166
#endif
166167
default:
167168
throw std::runtime_error(fmt::format("invalid audio api: {}", api));
@@ -172,8 +173,8 @@ std::vector<IAudioAPI::DeviceDescriptionPtr> IAudioAPI::GetDevices(AudioAPI api)
172173
{
173174
if (!IsAudioAPIAvailable(api))
174175
return {};
175-
176-
switch(api)
176+
177+
switch (api)
177178
{
178179
#if BOOST_OS_WINDOWS
179180
case DirectSound:
@@ -209,3 +210,51 @@ uint32 IAudioAPI::GetAudioDelay() const
209210
{
210211
return m_audioDelayOverride > 0 ? m_audioDelayOverride : s_audioDelay;
211212
}
213+
214+
AudioChannels IAudioAPI::AudioTypeToChannels(AudioType type)
215+
{
216+
auto& config = GetConfig();
217+
switch (type)
218+
{
219+
case TV:
220+
return config.tv_channels;
221+
case Gamepad:
222+
return config.pad_channels;
223+
case Portal:
224+
return kMono;
225+
default:
226+
return kMono;
227+
}
228+
}
229+
230+
std::wstring IAudioAPI::GetDeviceFromType(AudioType type)
231+
{
232+
auto& config = GetConfig();
233+
switch (type)
234+
{
235+
case TV:
236+
return config.tv_device;
237+
case Gamepad:
238+
return config.pad_device;
239+
case Portal:
240+
return config.portal_device;
241+
default:
242+
return L"";
243+
}
244+
}
245+
246+
sint32 IAudioAPI::GetVolumeFromType(AudioType type)
247+
{
248+
auto& config = GetConfig();
249+
switch (type)
250+
{
251+
case TV:
252+
return config.tv_volume;
253+
case Gamepad:
254+
return config.pad_volume;
255+
case Portal:
256+
return config.portal_volume;
257+
default:
258+
return 0;
259+
}
260+
}

src/audio/IAudioAPI.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <mmreg.h>
55
#endif
66

7+
#include "config/CemuConfig.h"
8+
79
class IAudioAPI
810
{
911
friend class GeneralSettings2;
@@ -30,6 +32,13 @@ class IAudioAPI
3032

3133
using DeviceDescriptionPtr = std::shared_ptr<DeviceDescription>;
3234

35+
enum AudioType
36+
{
37+
TV = 0,
38+
Gamepad,
39+
Portal
40+
};
41+
3342
enum AudioAPI
3443
{
3544
DirectSound = 0,
@@ -62,8 +71,8 @@ class IAudioAPI
6271
static void InitializeStatic();
6372
static bool IsAudioAPIAvailable(AudioAPI api);
6473

65-
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample);
66-
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
74+
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample);
75+
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
6776
static std::unique_ptr<IAudioAPI> CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
6877
static std::vector<DeviceDescriptionPtr> GetDevices(AudioAPI api);
6978

@@ -84,6 +93,9 @@ class IAudioAPI
8493
private:
8594
static uint32 s_audioDelay;
8695
void InitWFX(sint32 samplerate, sint32 channels, sint32 bits_per_sample);
96+
static AudioChannels AudioTypeToChannels(AudioType type);
97+
static std::wstring GetDeviceFromType(AudioType type);
98+
static sint32 GetVolumeFromType(AudioType type);
8799

88100
};
89101

@@ -93,3 +105,5 @@ extern AudioAPIPtr g_tvAudio;
93105

94106
extern AudioAPIPtr g_padAudio;
95107
extern std::atomic_int32_t g_padVolume;
108+
109+
extern AudioAPIPtr g_portalAudio;

src/config/CemuConfig.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
278278
tv_volume = audio.get("TVVolume", 20);
279279
pad_volume = audio.get("PadVolume", 0);
280280
input_volume = audio.get("InputVolume", 20);
281+
portal_volume = audio.get("PortalVolume", 20);
281282

282283
const auto tv = audio.get("TVDevice", "");
283284
try
@@ -309,6 +310,16 @@ void CemuConfig::Load(XMLConfigParser& parser)
309310
cemuLog_log(LogType::Force, "config load error: can't load input device: {}", input_device_name);
310311
}
311312

313+
const auto portal_device_name = audio.get("PortalDevice", "");
314+
try
315+
{
316+
portal_device = boost::nowide::widen(portal_device_name);
317+
}
318+
catch (const std::exception&)
319+
{
320+
cemuLog_log(LogType::Force, "config load error: can't load input device: {}", portal_device_name);
321+
}
322+
312323
// account
313324
auto acc = parser.get("Account");
314325
account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id);
@@ -511,9 +522,11 @@ void CemuConfig::Save(XMLConfigParser& parser)
511522
audio.set("TVVolume", tv_volume);
512523
audio.set("PadVolume", pad_volume);
513524
audio.set("InputVolume", input_volume);
525+
audio.set("PortalVolume", portal_volume);
514526
audio.set("TVDevice", boost::nowide::narrow(tv_device).c_str());
515527
audio.set("PadDevice", boost::nowide::narrow(pad_device).c_str());
516528
audio.set("InputDevice", boost::nowide::narrow(input_device).c_str());
529+
audio.set("PortalDevice", boost::nowide::narrow(portal_device).c_str());
517530

518531
// account
519532
auto acc = config.set("Account");

src/config/CemuConfig.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,8 @@ struct CemuConfig
480480
sint32 audio_api = 0;
481481
sint32 audio_delay = 2;
482482
AudioChannels tv_channels = kStereo, pad_channels = kStereo, input_channels = kMono;
483-
sint32 tv_volume = 50, pad_volume = 0, input_volume = 50;
484-
std::wstring tv_device{ L"default" }, pad_device, input_device;
483+
sint32 tv_volume = 50, pad_volume = 0, input_volume = 50, portal_volume = 50;
484+
std::wstring tv_device{ L"default" }, pad_device, input_device, portal_device;
485485

486486
// account
487487
struct

0 commit comments

Comments
 (0)