Skip to content
This repository

Fix WASAPI playback for 5.0 #1589

Closed
wants to merge 12 commits into from

8 participants

Karlson2k Damian Huckle bulkzooi Sascha Montellese da-anda Rainer Hochecker Martijn Kaijser Geoffrey McRae
Karlson2k
Collaborator

Not so elegant, but fully working.
WASAPI initialization was completely overhauled.

Karlson2k
Collaborator

@DDDamian To see how "best match" algorithm works, just allow usages of "double" in as.xml. As most hardware don't accept float and XBMC try to send float, you will see sequence of initialization attempts.

Damian Huckle
Collaborator

The opening statement concerns me ;)

This is a total re-write, without actually mentioning a feature or a bug being fixed. For some of it I like what you've done, some just adds complexity and in a few places out-right unnecessary code. Simple is good.

I'll go thru in detail and add line comments or general comments, but the first one is that neither advancedsetting seems to be actually required, nor does the 64bit audio at this point. I think it's good to add, but we don't need another setting just to determine whether we consider it. Ditto for the maxsample rate.

While I go thru this can you give me a brief synopsis on what bugs you are trying to fix or what feature(s) you add with this?

Damian Huckle
Collaborator

And yes, I know it is meant to fix the 5.0 FLAC issue ;)

Geoffrey McRae
Collaborator

Nice, this should be a separate PR as I would merge it in a second.

Damian Huckle
Collaborator

Agreed with gnif's comment above - that's a great standalone in the meantime :)

I've pulled locally for a thorough review and some testing for the wasapi code.

Karlson2k
Collaborator

As MSDN say, starting from WinXP (SP1 or SP2 - I don't remember) KSAUDIO_SPEAKER_5POINT1_SURROUND should be standard.
I checked - integrated realtek returns OK only for KSAUDIO_SPEAKER_5POINT1_SURROUND. Creative returns OK for both. In other cases I checked KSAUDIO_SPEAKER_5POINT1_SURROUND is a equals or better that KSAUDIO_SPEAKER_5POINT1.

Karlson2k
Collaborator

I checked format specifications and documentations, so everything is checked for correct values.

Collaborator

Not so sure, that it should be in WASAPI and not somewhere else in AE. But if it works fine here, it can be later moved to other place.

Karlson2k
Collaborator

@DDDamian Opening statement just mean that I wish to do more cleanup and change "goto" to more c-style if-else-while-return...

Karlson2k

This it a final check, but it's not correct. If sound card support 32-bit int, there is not guarantee that sound card will support 24-bit int with same freq/channels.

Karlson2k
Collaborator

Just move part of code to separate method.
Now it easier to modify it on move to somewhere else.

Karlson2k
Collaborator

More info about f4deec9.
First of all I move try and initialization code to same place. Sometimes card can't initialize with same parameters that was OK for IsFormatSupported. For example Creative cards report support for 48000/24-bit/5.1,but fail to use it. Now it's possible to try something else in that case.
Now, if format isn't supported we try following:
1. Try same freq, channels, but with bit-wider formats. For 16 bit we will try 24 bit, 32 bit and (if not disabled) even float and double. This is totally lossless (or almost lossless) and most preferred conversion.
2. If requested format don't have LFE, but user speakers configuration support LFE, that try to add LFE. This is absolutely lossless as now upmixing is needed in this case.
2.1. Try format + LFE with bit-wider (16->24->32..)
3. If requested format have SL SR but not BL BR, try to use BL BR instead of SL SR (or vice versa - SL SR instead of BL BR). This is close configurations and (soon) should be not up/down mixed.
3.1. Try side-back with bit-wider
3.2. Try side-back with LFE and bit-wider
4. Try full user speakers setup instead of format specified. As requested format contains only channels that user have, no upmixing should be required.
4.1. Try use speakers with bit-wider.
5. Try multiple of requested sample frequency. 22050->44100->88200... This is a almost perfect conversion with less possible harmonic distortion. For each frequency try bit-wider formats.
5.1. Try format + LFE with multiple of frequency and bit-wider.
5.2. Try side-back with multiple of frequency and bit-wider. Than try side-back + LFE with multiple of frequency and bit-wider.
5.3. Try user speakers with multiple of frequency and bit-wider.
6. Try format with standard known sample rates, that higher requested. This is not ideal transformations, but using higher frequency is better possible solution. For each frequency (sample rate) try bit-wider.
6.1. (Need to be added, not implemented now). For each frequency try +LFE, side-back, user speakers.
7. Try formats with lower bit resolution (float->32, 24->16).
7.1. For each format try +LFE, side-back, user speakers, multiple of frequency, standard frequencies but do not try bit-wider (as all bit-wider formats was already tried).
8. Try format with standard known sample rates, that lower requested. For each sample rate try wider bits.
9. Try formats with standard known sample rates, that lower requested and with lower bit resolution (float->32, 24->16). For each format try +LFE, side-back, user speakers, multiple of frequency, standard frForl equencies but do not try bit-wider or higher frequency (as all bit-wider and higher frequency formats was already tried).

So we try all possible options from best-match to worst case.

Karlson2k
Collaborator

Description of changes is here: xbmc#1589 (comment)

Damian Huckle
Collaborator

Thx karlson2k - it's certainly complete in terms of the range of parameters it checks. Short of the reported 5.0 flac issue I had seen no issues with finding the appropriate format previously.

I've been reviewing in general terms, but will have to "force" a few formats to test the extremities.

One other general impression: we already have enumerated all the device formats prior to doing the device initialization. It reports every supported data format, sample rate and channel count. Shouldn't we just use that?

This initialization needs to happen quite quickly when we switch tracks in the player. We already know what the device is capable of. A lot of unnecessary system calls for IsFormatSupported will add to that delay on format switching. This is why we enumerate the device capabilities on start-up, whereas this will happen with each hit of the skip button......

Karlson2k
Collaborator

@DDDamian The longest part of format checking is writing to log.
Even before I correct IsCompatible, Initialization was called twice on beginning of playback (once on start and immediately again as format was signaled as incompatible) and even with allowed float for PCM (so a lot of combinations was checked) there was not noticeable delay.

Unfortunately we can't check all possible combination of parameters. Separate check for frequencies, data formats and channels is not enough.
For example on Creative card stereo can be played back as 16-bit, 24(3), 24(4) and 32 on any frequency: 44.1, 48, 64, 88,2, 96. But 5.1 can be used only as 16/48 or 24/96 and fail on 24/44.1, 24/48 and on 16/44.1

I see the only possible way to avoid a lot of checks, this is to build a map of requested and resulted format. But we will need to reset this map with every user sound configuration change.

Damian Huckle
Collaborator

The plan is for the engine to store the max pcm bitdepth and sample rate returned by sink/device to prevent calling Initialization twice if float is rejected (as well as for an all-integer path). This still needs implementing in the engine though.

With the exception of the 64khz frequency noted above all those scenarios would have already worked with the current code. The difference would be that the current code would downsample rather than upsample if an exact match was not found for the sample rate, as you iterate upwards.

Again, these are just observations as well as some insight into other changes. With the exception of some complexity I'm liking what I see so far.

Karlson2k
Collaborator

@DDDamian Thanks for positive response!
Actually engine already stores all bitdepths and sample rates in CAEDeviceInfo.
I suggest to use CAEDeviceInfo& instead of std::string &device. It's easily to adapt current code for this.
And I'd like to extend CAEDeviceInfo a little.
Is it worth to try?

Karlson2k
Collaborator

Already in progress.
Basic idea is in 4ce647d

Damian Huckle
Collaborator

@Karlson2k - yep - that's what I was referring to in my previous comment (CAEDeviceInfo).

I'm perfectly fine with extending that class - we've barely touched on its original intent. Nice work!

Damian Huckle
Collaborator

@Karlson2k - check your email

bulkzooi

So this initiative died?

Sascha Montellese
Collaborator

@bulkzooi: Could you please stop spamming everyone unless you have something constructive to say about the code or the discussion? That would be nice, thanks.

bulkzooi

@Montellese: i thought pinging an 8 months old and untouched PR would be constructive. Especially given the 180 (and growing) open PR's. Rationale: otherwise github will end up as Trac once was.

da-anda
Collaborator

@fritsch @FernetMenta can this one be closed in favor of ActiveAE (where it's working I suppose, couldn't test)?

Rainer Hochecker
Collaborator

i had a brief look but not sure what this PR does fix or improve. e.g. 1c53efb seems wrong to me. due to pack func DTSHD requires 192khz and 8 channels. the mentioned commit does change those values.

Martijn Kaijser

i suggest we close this and if needed @Karlson2k will open a new PR based on ActiveAE

Edit:
best do this for all PRs that are based on SoftAE

Martijn Kaijser

If needed please create an updated PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
2  xbmc/cores/AudioEngine/AEAudioFormat.h
@@ -66,6 +66,8 @@ enum AEDataFormat
66 66
67 67 #define AE_IS_RAW(x) ((x) >= AE_FMT_AAC && (x) < AE_FMT_MAX)
68 68 #define AE_IS_RAW_HD(x) ((x) >= AE_FMT_EAC3 && (x) < AE_FMT_MAX)
  69 +#define AE_IS_INT(x) ((x) > AE_FMT_INVALID && (x) < AE_FMT_DOUBLE)
  70 +#define AE_IS_FLOAT(x) ((x) >= AE_FMT_DOUBLE && (x) <= AE_FMT_FLOAT)
69 71
70 72 /**
71 73 * The audio format structure that fully defines a stream's audio information
886 xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp
@@ -33,6 +33,9 @@
33 33 #include "threads/SingleLock.h"
34 34 #include "utils/CharsetConverter.h"
35 35 #include "../Utils/AEDeviceInfo.h"
  36 +#include "../AEFactory.h"
  37 +#include "../Engines/SoftAE/SoftAE.h"
  38 +#include "utils/SystemInfo.h"
36 39 #include <Mmreg.h>
37 40 #include <mmdeviceapi.h>
38 41
@@ -43,8 +46,15 @@ const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
43 46 const IID IID_IAudioClient = __uuidof(IAudioClient);
44 47 const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
45 48
46   -static const unsigned int WASAPISampleRateCount = 10;
47   -static const unsigned int WASAPISampleRates[] = {384000, 192000, 176400, 96000, 88200, 48000, 44100, 32000, 22050, 11025};
  49 +static const unsigned int WASAPITestSampleRates[] = {11025, 12000, 16000,
  50 + 22050, 24000, 32000,
  51 + 44100, 48000, 64000,
  52 + 88200, 96000, 128000,
  53 + 176400, 192000, 256000,
  54 + 352800, 384000};
  55 +static const unsigned int WASAPITestSampleRatesMaxIndex = SIZEOF_ARRAY(WASAPITestSampleRates) - 1; // 384000 Hz
  56 +static const unsigned int WASAPITestSampleRatesMaxSafeIndex1 = SIZEOF_ARRAY(WASAPITestSampleRates) - 4; // 192000 Hz
  57 +static const unsigned int WASAPITestSampleRatesMaxSafeIndex2 = SIZEOF_ARRAY(WASAPITestSampleRates) - 7; // 96000 Hz
48 58
49 59 #define WASAPI_SPEAKER_COUNT 21
50 60 static const unsigned int WASAPIChannelOrder[] = {AE_CH_RAW,
@@ -101,6 +111,15 @@ static const enum AEChannel layoutsList[][16] =
101 111 {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_LFE, AE_CH_SL, AE_CH_SR, AE_CH_BL, AE_CH_BR, AE_CH_TFL, AE_CH_TFR, AE_CH_TFC, AE_CH_TBL, AE_CH_TBR, AE_CH_TBC, AE_CH_TC, AE_CH_NULL} // Standard 7.1 + 3 front top + 3 back top + Top Center
102 112 };
103 113
  114 +static const unsigned int ac3bitrates[] = { 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000,
  115 + 160000, 192000, 224000, 256000, 320000, 384000, 448000, 512000, 576000, 640000,
  116 + 0 };
  117 +
  118 +static const unsigned int dtsbitrates[] = { 32000, 56000, 64000, 96000, 112000, 128000, 192000, 224000, 256000, 320000, 384000,
  119 + 448000, 512000, 576000, 640000, 768000, 960000, 1024000, 1152000, 1280000, 1344000,
  120 + 1408000, 1411200, 1472000, 1536000,
  121 + 0 };
  122 +
104 123 struct sampleFormat
105 124 {
106 125 GUID subFormat;
@@ -116,6 +135,13 @@ static const sampleFormat testFormats[] = { {KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 32
116 135 {KSDATAFORMAT_SUBTYPE_PCM, 24, 24, AE_FMT_S24NE3},
117 136 {KSDATAFORMAT_SUBTYPE_PCM, 16, 16, AE_FMT_S16NE} };
118 137
  138 +static const AEDataFormat testFormatSequence[] = { AE_FMT_S8, AE_FMT_S16NE, AE_FMT_S24NE3, AE_FMT_S24NE4, AE_FMT_S32NE, AE_FMT_FLOAT, AE_FMT_DOUBLE };
  139 +static const int testFormatSequenceIdxDouble = SIZEOF_ARRAY (testFormatSequence) - 1;
  140 +static const int testFormatSequenceIdxFloat = SIZEOF_ARRAY (testFormatSequence) - 2;
  141 +static const int testFormatSequenceIdx32Int = SIZEOF_ARRAY (testFormatSequence) - 3;
  142 +static const int testFormatSequenceIdx24Int = SIZEOF_ARRAY (testFormatSequence) - 4;
  143 +static const int testFormatSequenceIdx16Int = SIZEOF_ARRAY (testFormatSequence) - 6;
  144 +
119 145 struct winEndpointsToAEDeviceType
120 146 {
121 147 std::string winEndpointType;
@@ -190,6 +216,8 @@ CAESinkWASAPI::CAESinkWASAPI() :
190 216 m_isDirty(false)
191 217 {
192 218 m_channelLayout.Reset();
  219 + sinkReqFormat.m_dataFormat = AE_FMT_INVALID;
  220 + sinkReqFormat.m_channelLayout.Reset();
193 221 }
194 222
195 223 CAESinkWASAPI::~CAESinkWASAPI()
@@ -206,7 +234,7 @@ bool CAESinkWASAPI::Initialize(AEAudioFormat &format, std::string &device)
206 234
207 235 /* Save requested format */
208 236 /* Clear returned format */
209   - sinkReqFormat = format.m_dataFormat;
  237 + sinkReqFormat = format;
210 238 sinkRetFormat = AE_FMT_INVALID;
211 239
212 240 IMMDeviceEnumerator* pEnumerator = NULL;
@@ -355,18 +383,20 @@ bool CAESinkWASAPI::IsCompatible(const AEAudioFormat format, const std::string d
355 383 return false;
356 384
357 385 u_int notCompatible = 0;
  386 + u_int notCompatibleReq = 0;
358 387 const u_int numTests = 5;
359 388 std::string strDiffBecause ("");
360   - static const char* compatibleParams[numTests] = {":Devices",
361   - ":Channels",
362   - ":Sample Rates",
363   - ":Data Formats",
364   - ":Passthrough Formats"};
365   -
366   - notCompatible = (notCompatible +!((AE_IS_RAW(format.m_dataFormat) == AE_IS_RAW(m_encodedFormat)) ||
367   - (!AE_IS_RAW(format.m_dataFormat) == !AE_IS_RAW(m_encodedFormat)))) << 1;
368   - notCompatible = (notCompatible +!((sinkReqFormat == format.m_dataFormat) &&
369   - (sinkRetFormat == m_format.m_dataFormat))) << 1;
  389 + std::string strDiffBecauseReq ("");
  390 + static const char* compatibleParams[numTests] = {"Devices, ",
  391 + "Channels, ",
  392 + "Sample Rates, ",
  393 + "Data Formats, ",
  394 + "Passthrough Formats, "};
  395 +
  396 + notCompatible = (notCompatible + !(!AE_IS_RAW(format.m_dataFormat) ||
  397 + m_encodedFormat == format.m_dataFormat)) << 1;
  398 + notCompatible = (notCompatible + !(AE_IS_RAW(format.m_dataFormat) ||
  399 + sinkRetFormat == format.m_dataFormat)) << 1;
370 400 notCompatible = (notCompatible + !(format.m_sampleRate == m_format.m_sampleRate)) << 1;
371 401 notCompatible = (notCompatible + !(format.m_channelLayout.Count() == m_format.m_channelLayout.Count())) << 1;
372 402 notCompatible = (notCompatible + !(m_device == device));
@@ -377,13 +407,36 @@ bool CAESinkWASAPI::IsCompatible(const AEAudioFormat format, const std::string d
377 407 return true;
378 408 }
379 409
  410 + notCompatibleReq = (notCompatibleReq + !(!AE_IS_RAW(format.m_dataFormat) ||
  411 + sinkReqFormat.m_dataFormat == format.m_dataFormat)) << 1;
  412 + notCompatibleReq = (notCompatibleReq + !(AE_IS_RAW(format.m_dataFormat) ||
  413 + sinkReqFormat.m_dataFormat == format.m_dataFormat)) << 1;
  414 + notCompatibleReq = (notCompatibleReq + !(format.m_sampleRate == sinkReqFormat.m_sampleRate)) << 1;
  415 + notCompatibleReq = (notCompatibleReq + !(format.m_channelLayout.Count() == sinkReqFormat.m_channelLayout.Count())) << 1;
  416 + notCompatibleReq = (notCompatibleReq + !(m_device == device));
  417 +
  418 + if (!notCompatibleReq)
  419 + {
  420 + CLog::Log(LOGDEBUG, __FUNCTION__": Format compatible with last requested format - assuming same initialization and reusing existing sink");
  421 + return true;
  422 + }
  423 +
380 424 for (int i = 0; i < numTests ; i++)
381 425 {
382   - strDiffBecause += (notCompatible & 0x01) ? (std::string) compatibleParams[i] : "";
383   - notCompatible = notCompatible >> 1;
  426 + if (notCompatible & 0x01)
  427 + strDiffBecause += std::string (compatibleParams[i]);
  428 + if (notCompatibleReq & 0x01)
  429 + strDiffBecauseReq += std::string (compatibleParams[i]);
  430 + notCompatible >>= 1;
  431 + notCompatibleReq >>= 1;
384 432 }
  433 + strDiffBecause.pop_back();
  434 + strDiffBecause.pop_back();
  435 + strDiffBecauseReq.pop_back();
  436 + strDiffBecauseReq.pop_back();
385 437
386   - CLog::Log(LOGDEBUG, __FUNCTION__": Formats Incompatible due to different %s", strDiffBecause.c_str());
  438 + CLog::Log(LOGDEBUG, __FUNCTION__": Format Incompatible with current format due to different: %s.", strDiffBecause.c_str());
  439 + CLog::Log(LOGDEBUG, __FUNCTION__": Format Incompatible with last requested format due to different: %s.", strDiffBecauseReq.c_str());
387 440 return false;
388 441 }
389 442
@@ -678,7 +731,7 @@ void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList)
678 731
679 732 /* Test format DTS */
680 733 wfxex.Format.nSamplesPerSec = 48000;
681   - wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
  734 + wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
682 735 wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DTS;
683 736 wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
684 737 wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
@@ -736,13 +789,13 @@ void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList)
736 789 wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
737 790 wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
738 791
739   - for (int j = 0; j < WASAPISampleRateCount; j++)
  792 + for (int j = 0; j <= WASAPITestSampleRatesMaxIndex; j++)
740 793 {
741   - wfxex.Format.nSamplesPerSec = WASAPISampleRates[j];
  794 + wfxex.Format.nSamplesPerSec = WASAPITestSampleRates[j];
742 795 wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
743 796 hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
744 797 if (SUCCEEDED(hr))
745   - deviceInfo.m_sampleRates.push_back(WASAPISampleRates[j]);
  798 + deviceInfo.m_sampleRates.push_back(WASAPITestSampleRates[j]);
746 799 }
747 800
748 801 /* Test format for channels iteration */
@@ -849,7 +902,7 @@ void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList)
849 902
850 903 //Private utility functions////////////////////////////////////////////////////
851 904
852   -void CAESinkWASAPI::BuildWaveFormatExtensible(AEAudioFormat &format, WAVEFORMATEXTENSIBLE &wfxex)
  905 +void CAESinkWASAPI::BuildWaveFormatExtensible(const AEAudioFormat &format, WAVEFORMATEXTENSIBLE &wfxex)
853 906 {
854 907 wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
855 908 wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
@@ -857,18 +910,18 @@ void CAESinkWASAPI::BuildWaveFormatExtensible(AEAudioFormat &format, WAVEFORMATE
857 910
858 911 if (!AE_IS_RAW(format.m_dataFormat)) // PCM data
859 912 {
860   - wfxex.dwChannelMask = SpeakerMaskFromAEChannels(format.m_channelLayout);
  913 + wfxex.dwChannelMask = GetSpeakerMaskFromAEChannels(format.m_channelLayout);
861 914 wfxex.Format.nChannels = (WORD)format.m_channelLayout.Count();
862 915 wfxex.Format.nSamplesPerSec = format.m_sampleRate;
863 916 wfxex.Format.wBitsPerSample = CAEUtil::DataFormatToBits((AEDataFormat) format.m_dataFormat);
864   - wfxex.SubFormat = format.m_dataFormat <= AE_FMT_FLOAT ? KSDATAFORMAT_SUBTYPE_PCM : KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  917 + wfxex.SubFormat = format.m_dataFormat < AE_FMT_FLOAT ? KSDATAFORMAT_SUBTYPE_PCM : KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
865 918 }
866 919 else //Raw bitstream
867 920 {
868 921 wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
869 922 if (format.m_dataFormat == AE_FMT_AC3 || format.m_dataFormat == AE_FMT_DTS)
870 923 {
871   - wfxex.dwChannelMask = bool (format.m_channelLayout.Count() == 2) ? KSAUDIO_SPEAKER_STEREO : KSAUDIO_SPEAKER_5POINT1;
  924 + wfxex.dwChannelMask = bool (format.m_channelLayout.Count() == 2) ? KSAUDIO_SPEAKER_STEREO : KSAUDIO_SPEAKER_5POINT1_SURROUND;
872 925
873 926 if (format.m_dataFormat == AE_FMT_AC3)
874 927 {
@@ -897,7 +950,7 @@ void CAESinkWASAPI::BuildWaveFormatExtensible(AEAudioFormat &format, WAVEFORMATE
897 950 case AE_FMT_EAC3:
898 951 wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS;
899 952 wfxex.Format.nChannels = 2; // One IEC 60958 Line.
900   - wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
  953 + wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
901 954 break;
902 955 case AE_FMT_TRUEHD:
903 956 wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP;
@@ -914,11 +967,11 @@ void CAESinkWASAPI::BuildWaveFormatExtensible(AEAudioFormat &format, WAVEFORMATE
914 967 if (format.m_channelLayout.Count() == 8)
915 968 wfxex.dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
916 969 else
917   - wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
  970 + wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
918 971 }
919 972 }
920 973
921   - if (wfxex.Format.wBitsPerSample == 32 && wfxex.SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
  974 + if (format.m_dataFormat == AE_FMT_S24BE3 || format.m_dataFormat == AE_FMT_S24LE3 || format.m_dataFormat == AE_FMT_S24NE3)
922 975 wfxex.Samples.wValidBitsPerSample = 24;
923 976 else
924 977 wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample;
@@ -927,116 +980,61 @@ void CAESinkWASAPI::BuildWaveFormatExtensible(AEAudioFormat &format, WAVEFORMATE
927 980 wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
928 981 }
929 982
930   -void CAESinkWASAPI::BuildWaveFormatExtensibleIEC61397(AEAudioFormat &format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex)
  983 +void CAESinkWASAPI::BuildWaveFormatExtensibleIEC61397(const AEAudioFormat &format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex)
931 984 {
  985 + ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE_IEC61937));
932 986 /* Fill the common structure */
933 987 BuildWaveFormatExtensible(format, wfxex.FormatExt);
934   -
935   - /* Code below kept for future use - preferred for later Windows versions */
936   - /* but can cause problems on older Windows versions and drivers */
937   - /*
938   - wfxex.FormatExt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE_IEC61937)-sizeof(WAVEFORMATEX);
939   - wfxex.dwEncodedChannelCount = format.m_channelLayout.Count();
940   - wfxex.dwEncodedSamplesPerSec = bool(format.m_dataFormat == AE_FMT_TRUEHD ||
941   - format.m_dataFormat == AE_FMT_DTSHD ||
942   - format.m_dataFormat == AE_FMT_EAC3) ? 96000L : 48000L;
943   - wfxex.dwAverageBytesPerSec = 0; //Ignored */
  988 +
  989 + if (AE_IS_RAW(format.m_dataFormat) && CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin7))
  990 + {
  991 + wfxex.FormatExt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE_IEC61937)-sizeof(WAVEFORMATEX);
  992 + wfxex.dwEncodedChannelCount = format.m_channelLayout.Count();
  993 + wfxex.dwEncodedSamplesPerSec = format.m_encodedRate;
  994 + wfxex.dwAverageBytesPerSec = 0; //Ignored
  995 + }
944 996 }
945 997
946 998 bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format)
947   -{
  999 + {
  1000 + CheckAndCorrectFormat(format);
  1001 +
948 1002 WAVEFORMATEXTENSIBLE_IEC61937 wfxex_iec61937;
949 1003 WAVEFORMATEXTENSIBLE &wfxex = wfxex_iec61937.FormatExt;
950 1004
951   - if (format.m_dataFormat <= AE_FMT_FLOAT)
952   - BuildWaveFormatExtensible(format, wfxex);
953   - else
954   - BuildWaveFormatExtensibleIEC61397(format, wfxex_iec61937);
955   -
956   - /* Test for incomplete format and provide defaults */
957   - if (format.m_sampleRate == 0 ||
958   - format.m_channelLayout == NULL ||
959   - format.m_dataFormat <= AE_FMT_INVALID ||
960   - format.m_dataFormat >= AE_FMT_MAX ||
961   - format.m_channelLayout.Count() == 0)
962   - {
963   - wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
964   - wfxex.Format.nChannels = 2;
965   - wfxex.Format.nSamplesPerSec = 44100L;
966   - wfxex.Format.wBitsPerSample = 16;
967   - wfxex.Format.nBlockAlign = 4;
968   - wfxex.Samples.wValidBitsPerSample = 16;
969   - wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
970   - wfxex.Format.nAvgBytesPerSec = wfxex.Format.nBlockAlign * wfxex.Format.nSamplesPerSec;
971   - wfxex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
972   - wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
973   - }
974   -
975   - HRESULT hr = m_pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
  1005 + // Forward declaration to avoid compiler error
  1006 + AEAudioFormat formatMod;
976 1007
  1008 + HRESULT hr = TryAndInitializeExclusive(format, wfxex_iec61937);
977 1009 if (SUCCEEDED(hr))
978   - {
979   - CLog::Log(LOGINFO, __FUNCTION__": Format is Supported - will attempt to Initialize");
980 1010 goto initialize;
981   - }
982   - else if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) //It failed for a reason unrelated to an unsupported format.
983   - {
984   - CLog::Log(LOGERROR, __FUNCTION__": IsFormatSupported failed (%s)", WASAPIErrToStr(hr));
985   - return false;
986   - }
987 1011 else if (AE_IS_RAW(format.m_dataFormat)) //No sense in trying other formats for passthrough.
988 1012 return false;
989 1013
990 1014 CLog::Log(LOGERROR, __FUNCTION__": IsFormatSupported failed (%s) - trying to find a compatible format", WASAPIErrToStr(hr));
991 1015
992   - int closestMatch;
993   -
994   - /* The requested format is not supported by the device. Find something that works */
995   - for (int j = 0; j < sizeof(testFormats)/sizeof(sampleFormat); j++)
996   - {
997   - closestMatch = -1;
  1016 + /* Simplify format */
  1017 + formatMod = SimplifyFormat(format);
998 1018
999   - wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1000   - wfxex.SubFormat = testFormats[j].subFormat;
1001   - wfxex.Format.wBitsPerSample = testFormats[j].bitsPerSample;
1002   - wfxex.Samples.wValidBitsPerSample = testFormats[j].validBitsPerSample;
1003   - wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
  1019 + /* Try best possible formats */
  1020 + if (FindCompatibleFormatAlmostLossless(formatMod, wfxex_iec61937, TryHigherSampleRates, true))
  1021 + goto initialize;
1004 1022
1005   - for (int i = 0 ; i < WASAPISampleRateCount; i++)
1006   - {
1007   - wfxex.Format.nSamplesPerSec = WASAPISampleRates[i];
1008   - wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
1009   -
1010   - /* Trace format match iteration loop via log */
1011   - #if 0
1012   - CLog::Log(LOGDEBUG, "WASAPI: Trying Format: %s, %d, %d, %d", CAEUtil::DataFormatToStr(testFormats[j].subFormatType),
1013   - wfxex.Format.nSamplesPerSec,
1014   - wfxex.Format.wBitsPerSample,
1015   - wfxex.Samples.wValidBitsPerSample);
1016   - #endif
  1023 + /* Try all standard sample rates, higher than requested */
  1024 + if (FindCompatibleFormatAnySampleRate(formatMod, wfxex_iec61937, false, TryHigherBits, true))
  1025 + goto initialize;
1017 1026
1018   - hr = m_pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
  1027 + /* Try all data formats */
  1028 + if (FindCompatibleFormatAnyDataFormat(formatMod, wfxex_iec61937, false, true))
  1029 + goto initialize;
1019 1030
1020   - if (SUCCEEDED(hr))
1021   - {
1022   - /* If the current sample rate matches the source then stop looking and use it */
1023   - if ((WASAPISampleRates[i] == format.m_sampleRate) && (testFormats[j].subFormatType <= format.m_dataFormat))
1024   - goto initialize;
1025   - /* If this rate is closer to the source then the previous one, save it */
1026   - else if (closestMatch < 0 || abs((int)WASAPISampleRates[i] - (int)format.m_sampleRate) < abs((int)WASAPISampleRates[closestMatch] - (int)format.m_sampleRate))
1027   - closestMatch = i;
1028   - }
1029   - else if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
1030   - CLog::Log(LOGERROR, __FUNCTION__": IsFormatSupported failed (%s)", WASAPIErrToStr(hr));
1031   - }
  1031 + /* Forced to try lower sample rates */
  1032 + if (FindCompatibleFormatAnySampleRate(formatMod, wfxex_iec61937, true, TryHigherBits, true))
  1033 + goto initialize;
1032 1034
1033   - if (closestMatch >= 0)
1034   - {
1035   - wfxex.Format.nSamplesPerSec = WASAPISampleRates[closestMatch];
1036   - wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
1037   - goto initialize;
1038   - }
1039   - }
  1035 + /* Worst case - lower sample rate and lower bit resolution */
  1036 + if (FindCompatibleFormatAnyDataFormat(formatMod, wfxex_iec61937, true, true))
  1037 + goto initialize;
1040 1038
1041 1039 CLog::Log(LOGERROR, __FUNCTION__": Unable to locate a supported output format for the device. Check the speaker settings in the control panel.");
1042 1040
@@ -1046,20 +1044,13 @@ bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format)
1046 1044
1047 1045 initialize:
1048 1046
1049   - AEChannelsFromSpeakerMask(wfxex.dwChannelMask);
1050   -
1051   - /* When the stream is raw, the values in the format structure are set to the link */
1052   - /* parameters, so store the encoded stream values here for the IsCompatible function */
1053   - m_encodedFormat = format.m_dataFormat;
1054   - m_encodedChannels = wfxex.Format.nChannels;
1055   - m_encodedSampleRate = format.m_encodedRate;
1056   - wfxex_iec61937.dwEncodedChannelCount = wfxex.Format.nChannels;
1057   - wfxex_iec61937.dwEncodedSamplesPerSec = m_encodedSampleRate;
1058   -
1059 1047 /* Set up returned sink format for engine */
  1048 + format.m_channelLayout = m_channelLayout;
1060 1049 if (!AE_IS_RAW(format.m_dataFormat))
1061 1050 {
1062   - if (wfxex.Format.wBitsPerSample == 32)
  1051 + if (wfxex.Format.wBitsPerSample == 64)
  1052 + format.m_dataFormat = AE_FMT_DOUBLE;
  1053 + else if (wfxex.Format.wBitsPerSample == 32)
1063 1054 {
1064 1055 if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
1065 1056 format.m_dataFormat = AE_FMT_FLOAT;
@@ -1077,22 +1068,104 @@ bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format)
1077 1068 format.m_sampleRate = wfxex.Format.nSamplesPerSec; //PCM: Sample rate. RAW: Link speed
1078 1069 format.m_frameSize = (wfxex.Format.wBitsPerSample >> 3) * wfxex.Format.nChannels;
1079 1070
  1071 + if (AE_IS_RAW(format.m_dataFormat))
  1072 + format.m_dataFormat = AE_FMT_S16NE;
  1073 +
  1074 + return true;
  1075 +}
  1076 +
  1077 +HRESULT CAESinkWASAPI::TryAndInitializeExclusive(const AEAudioFormat &format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex_iec61937)
  1078 +{
  1079 + if (!AE_IS_RAW(format.m_dataFormat))
  1080 + {
  1081 + if (format.m_sampleRate > (unsigned int) g_advancedSettings.m_WASAPIMaximumPCMSampleRate)
  1082 + return AUDCLNT_E_UNSUPPORTED_FORMAT;
  1083 +
  1084 + switch (format.m_dataFormat)
  1085 + {
  1086 + case AE_FMT_U8:
  1087 + case AE_FMT_S8:
  1088 + case AE_FMT_S16BE:
  1089 + case AE_FMT_S16LE:
  1090 + case AE_FMT_S16NE: break;
  1091 +
  1092 + case AE_FMT_S32BE:
  1093 + case AE_FMT_S32LE:
  1094 + case AE_FMT_S32NE:
  1095 + if (g_advancedSettings.m_WASAPIExclusiveMaximumBits < 32)
  1096 + return AUDCLNT_E_UNSUPPORTED_FORMAT;
  1097 + break;
  1098 +
  1099 + case AE_FMT_S24BE4:
  1100 + case AE_FMT_S24LE4:
  1101 + case AE_FMT_S24NE4:
  1102 + case AE_FMT_S24BE3:
  1103 + case AE_FMT_S24LE3:
  1104 + case AE_FMT_S24NE3:
  1105 + if (g_advancedSettings.m_WASAPIExclusiveMaximumBits < 24)
  1106 + return AUDCLNT_E_UNSUPPORTED_FORMAT;
  1107 + break;
  1108 +
  1109 + case AE_FMT_DOUBLE:
  1110 + if (g_advancedSettings.m_WASAPIExclusiveMaximumBits < 64)
  1111 + return AUDCLNT_E_UNSUPPORTED_FORMAT;
  1112 + break;
  1113 +
  1114 + case AE_FMT_FLOAT:
  1115 + if (g_advancedSettings.m_WASAPIExclusiveMaximumBits < 33)
  1116 + return AUDCLNT_E_UNSUPPORTED_FORMAT;
  1117 + break;
  1118 + }
  1119 + }
  1120 + HRESULT hr;
  1121 + BuildWaveFormatExtensibleIEC61397(format, wfxex_iec61937);
  1122 +#ifdef _DEBUG
  1123 + CLog::Log(LOGDEBUG, "WASAPI: Trying Format: %s, %d Hz, %d bits, %d valid bits, %d channels, channel mask: %04X.", CAEUtil::DataFormatToStr(format.m_dataFormat),
  1124 + wfxex_iec61937.FormatExt.Format.nSamplesPerSec, wfxex_iec61937.FormatExt.Format.wBitsPerSample, wfxex_iec61937.FormatExt.Samples.wValidBitsPerSample,
  1125 + wfxex_iec61937.FormatExt.Format.nChannels, wfxex_iec61937.FormatExt.dwChannelMask);
  1126 +#endif
  1127 + hr = m_pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX *)&wfxex_iec61937, NULL);
  1128 + if (FAILED(hr))
  1129 + return hr;
  1130 +
  1131 + /* Sometimes IsFormatSupported returns OK, but device initialization will fail */
  1132 + /* So try to initialize device and, if failed, try other formats if appropriate */
  1133 +
  1134 + AEChannelsFromSpeakerMask(wfxex_iec61937.FormatExt.dwChannelMask);
  1135 +
1080 1136 REFERENCE_TIME audioSinkBufferDurationMsec, hnsLatency;
1081 1137
1082 1138 /* Get m_audioSinkBufferSizeMsec from advancedsettings.xml */
1083 1139 audioSinkBufferDurationMsec = (REFERENCE_TIME)g_advancedSettings.m_audioSinkBufferDurationMsec * 10000;
1084 1140
1085 1141 /* Use advancedsetting value for buffer size as long as it's over minimum set above */
  1142 + unsigned int frameSize = (wfxex_iec61937.FormatExt.Format.wBitsPerSample >> 3) * wfxex_iec61937.FormatExt.Format.nChannels;
1086 1143 audioSinkBufferDurationMsec = (REFERENCE_TIME)std::max(audioSinkBufferDurationMsec, (REFERENCE_TIME)500000);
1087   - audioSinkBufferDurationMsec = (REFERENCE_TIME)((audioSinkBufferDurationMsec / format.m_frameSize) * format.m_frameSize); //even number of frames
1088   -
1089   - if (AE_IS_RAW(format.m_dataFormat))
1090   - format.m_dataFormat = AE_FMT_S16NE;
  1144 + audioSinkBufferDurationMsec = (REFERENCE_TIME)((audioSinkBufferDurationMsec / frameSize) * frameSize); //even number of frames
1091 1145
  1146 + /* Open the stream and associate it with an audio session */
1092 1147 hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
1093   - audioSinkBufferDurationMsec, audioSinkBufferDurationMsec, &wfxex.Format, NULL);
  1148 + audioSinkBufferDurationMsec, audioSinkBufferDurationMsec, (WAVEFORMATEX *)&wfxex_iec61937, NULL);
1094 1149
1095   - if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
  1150 + if (hr == AUDCLNT_E_ALREADY_INITIALIZED)
  1151 + {
  1152 + /* Release the previous allocations */
  1153 + SAFE_RELEASE(m_pAudioClient);
  1154 +
  1155 + /* Create a new audio client */
  1156 + hr = m_pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&m_pAudioClient);
  1157 + if (FAILED(hr))
  1158 + {
  1159 + CLog::Log(LOGERROR, __FUNCTION__": Device Activation Failed : %s", WASAPIErrToStr(hr));
  1160 + return hr;
  1161 + }
  1162 +
  1163 + /* Try one more time */
  1164 + hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
  1165 + audioSinkBufferDurationMsec, audioSinkBufferDurationMsec, (WAVEFORMATEX *)&wfxex_iec61937, NULL);
  1166 + }
  1167 +
  1168 + if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED || (FAILED (hr) && CSysInfo::IsWindowsVersion(CSysInfo::WindowsVersionVista)))
1096 1169 {
1097 1170 /* WASAPI requires aligned buffer */
1098 1171 /* Get the next aligned frame */
@@ -1100,10 +1173,10 @@ bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format)
1100 1173 if (FAILED(hr))
1101 1174 {
1102 1175 CLog::Log(LOGERROR, __FUNCTION__": GetBufferSize Failed : %s", WASAPIErrToStr(hr));
1103   - return false;
  1176 + return hr;
1104 1177 }
1105 1178
1106   - audioSinkBufferDurationMsec = (REFERENCE_TIME) ((10000.0 * 1000 / wfxex.Format.nSamplesPerSec * m_uiBufferLen) + 0.5);
  1179 + audioSinkBufferDurationMsec = (REFERENCE_TIME) ((10000.0 * 1000 / wfxex_iec61937.FormatExt.Format.nSamplesPerSec * m_uiBufferLen) + 0.5);
1107 1180
1108 1181 /* Release the previous allocations */
1109 1182 SAFE_RELEASE(m_pAudioClient);
@@ -1113,32 +1186,22 @@ bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format)
1113 1186 if (FAILED(hr))
1114 1187 {
1115 1188 CLog::Log(LOGERROR, __FUNCTION__": Device Activation Failed : %s", WASAPIErrToStr(hr));
1116   - return false;
  1189 + return hr;
1117 1190 }
1118 1191
1119 1192 /* Open the stream and associate it with an audio session */
1120 1193 hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
1121   - audioSinkBufferDurationMsec, audioSinkBufferDurationMsec, &wfxex.Format, NULL);
  1194 + audioSinkBufferDurationMsec, audioSinkBufferDurationMsec, (WAVEFORMATEX *)&wfxex_iec61937, NULL);
1122 1195 }
  1196 +
1123 1197 if (FAILED(hr))
1124   - {
1125   - CLog::Log(LOGERROR, __FUNCTION__": Failed to initialize WASAPI in exclusive mode %d - (%s).", HRESULT(hr), WASAPIErrToStr(hr));
1126   - CLog::Log(LOGDEBUG, " Sample Rate : %d", wfxex.Format.nSamplesPerSec);
1127   - CLog::Log(LOGDEBUG, " Sample Format : %s", CAEUtil::DataFormatToStr(format.m_dataFormat));
1128   - CLog::Log(LOGDEBUG, " Bits Per Sample : %d", wfxex.Format.wBitsPerSample);
1129   - CLog::Log(LOGDEBUG, " Valid Bits/Samp : %d", wfxex.Samples.wValidBitsPerSample);
1130   - CLog::Log(LOGDEBUG, " Channel Count : %d", wfxex.Format.nChannels);
1131   - CLog::Log(LOGDEBUG, " Block Align : %d", wfxex.Format.nBlockAlign);
1132   - CLog::Log(LOGDEBUG, " Avg. Bytes Sec : %d", wfxex.Format.nAvgBytesPerSec);
1133   - CLog::Log(LOGDEBUG, " Samples/Block : %d", wfxex.Samples.wSamplesPerBlock);
1134   - CLog::Log(LOGDEBUG, " Format cBSize : %d", wfxex.Format.cbSize);
1135   - CLog::Log(LOGDEBUG, " Channel Layout : %s", ((std::string)format.m_channelLayout).c_str());
1136   - CLog::Log(LOGDEBUG, " Enc. Channels : %d", wfxex_iec61937.dwEncodedChannelCount);
1137   - CLog::Log(LOGDEBUG, " Enc. Samples/Sec: %d", wfxex_iec61937.dwEncodedSamplesPerSec);
1138   - CLog::Log(LOGDEBUG, " Channel Mask : %d", wfxex.dwChannelMask);
1139   - CLog::Log(LOGDEBUG, " Periodicty : %d", audioSinkBufferDurationMsec);
1140   - return false;
1141   - }
  1198 + return hr;
  1199 +
  1200 + /* When the stream is raw, the values in the format structure are set to the link */
  1201 + /* parameters, so store the encoded stream values here for the IsCompatible function */
  1202 + m_encodedFormat = format.m_dataFormat;
  1203 + m_encodedChannels = format.m_channelLayout.Count();
  1204 + m_encodedSampleRate = format.m_encodedRate;
1142 1205
1143 1206 /* Latency of WASAPI buffers in event-driven mode is equal to the returned value */
1144 1207 /* of GetStreamLatency converted from 100ns intervals to seconds then multiplied */
@@ -1148,13 +1211,342 @@ bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format)
1148 1211 hr = m_pAudioClient->GetStreamLatency(&hnsLatency);
1149 1212 m_sinkLatency = hnsLatency * 0.0000002;
1150 1213
1151   - CLog::Log(LOGINFO, __FUNCTION__": WASAPI Exclusive Mode Sink Initialized using: %s, %d, %d",
1152   - CAEUtil::DataFormatToStr(format.m_dataFormat),
1153   - wfxex.Format.nSamplesPerSec,
1154   - wfxex.Format.nChannels);
1155   - return true;
  1214 + CLog::Log(LOGINFO, __FUNCTION__": WASAPI Exclusive Mode Sink Initialized using: %s, %d Hz, %d bits, %d valid bits, %d channels, channel mask: %04X.", CAEUtil::DataFormatToStr(format.m_dataFormat),
  1215 + wfxex_iec61937.FormatExt.Format.nSamplesPerSec, wfxex_iec61937.FormatExt.Format.wBitsPerSample, wfxex_iec61937.FormatExt.Samples.wValidBitsPerSample,
  1216 + wfxex_iec61937.FormatExt.Format.nChannels, wfxex_iec61937.FormatExt.dwChannelMask);
  1217 +
  1218 + return hr;
  1219 +}
  1220 +
  1221 +AEAudioFormat CAESinkWASAPI::SimplifyFormat(const AEAudioFormat &format)
  1222 +{
  1223 + AEAudioFormat newFormat = format;
  1224 + switch (format.m_dataFormat)
  1225 + {
  1226 + case AE_FMT_U8:
  1227 + newFormat.m_dataFormat = AE_FMT_S8;
  1228 + break;
  1229 + case AE_FMT_S16BE:
  1230 + case AE_FMT_S16LE:
  1231 + newFormat.m_dataFormat = AE_FMT_S16NE;
  1232 + break;
  1233 + case AE_FMT_S24BE3:
  1234 + case AE_FMT_S24LE3:
  1235 + newFormat.m_dataFormat = AE_FMT_S24NE3;
  1236 + break;
  1237 + case AE_FMT_S24BE4:
  1238 + case AE_FMT_S24LE4:
  1239 + newFormat.m_dataFormat = AE_FMT_S24NE4;
  1240 + break;
  1241 + case AE_FMT_S32BE:
  1242 + case AE_FMT_S32LE:
  1243 + newFormat.m_dataFormat = AE_FMT_S32NE;
  1244 + break;
  1245 + }
  1246 +
  1247 + return newFormat;
  1248 +}
  1249 +
  1250 +bool CAESinkWASAPI::FindCompatibleFormatAlmostLossless(AEAudioFormat format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex, SearchStrategy strategy, bool formaitIsSimplified /*= false*/)
  1251 +{
  1252 + CAEChannelInfo userSelectedLayout = ((CSoftAE*)CAEFactory::GetEngine())->GetStdChLayout();
  1253 +
  1254 + if (!formaitIsSimplified)
  1255 + format = SimplifyFormat(format);
  1256 + if (strategy < TryHigherSampleRatesWODefault)
  1257 + {
  1258 + /* Try with higher bits resolution as lossless conversion */
  1259 + if (strategy == DefaultOnly)
  1260 + {
  1261 + if (SUCCEEDED(TryAndInitializeExclusive(format, wfxex)))
  1262 + return true;
  1263 + }
  1264 + else
  1265 + if (FindCompatibleFormatHigherBits(format, wfxex, true, true))
  1266 + return true;
  1267 +
  1268 + /* Try to add LFE if not present */
  1269 + if (FindCompatibleFormatWithLFE(format, wfxex, std::min(strategy, TryHigherBits), true))
  1270 + return true;
  1271 +
  1272 + /* Try to replace SL SR with BL BR or vice versa */
  1273 + if (FindCompatibleFormatSideOrBack(format, wfxex, std::min(strategy, TryHigherBits), true))
  1274 + return true;
  1275 +
  1276 + /* Try with full user channels layout */
  1277 + if (format.m_channelLayout != userSelectedLayout)
  1278 + {
  1279 + AEAudioFormat formatTest = format;
  1280 + formatTest.m_channelLayout = userSelectedLayout;
  1281 + if (strategy == DefaultOnly)
  1282 + {
  1283 + if (SUCCEEDED(TryAndInitializeExclusive(format, wfxex)))
  1284 + return true;
  1285 + }
  1286 + else
  1287 + {
  1288 + if (FindCompatibleFormatHigherBits(formatTest, wfxex, true, true))
  1289 + return true;
  1290 + }
  1291 + }
  1292 + }
  1293 +
  1294 + if (strategy >= TryHigherSampleRates)
  1295 + {
  1296 + /* Try this combinations more time, now with higher sample rates */
  1297 + /* In each function skip sample rates, that already checked */
  1298 + /* First try multiple of requested rate as less distortive */
  1299 + if (FindCompatibleFormatHigherSampleRate(format, wfxex, std::max(strategy, TryHigherSampleRatesWODefault), true))
  1300 + return true;
  1301 +
  1302 + /* Try to add LFE if not present */
  1303 + if (FindCompatibleFormatWithLFE(format, wfxex, std::max(strategy, TryHigherSampleRatesWODefault), true))
  1304 + return true;
  1305 +
  1306 + /* Try to replace SL SR with BL BR or vice versa */
  1307 + if (FindCompatibleFormatSideOrBack(format, wfxex, std::max(strategy, TryHigherSampleRatesWODefault), true))
  1308 + return true;
  1309 +
  1310 + /* Try with full user channels layout */
  1311 + if (format.m_channelLayout != userSelectedLayout)
  1312 + {
  1313 + AEAudioFormat formatTest = format;
  1314 + formatTest.m_channelLayout = userSelectedLayout;
  1315 + if (FindCompatibleFormatHigherSampleRate(formatTest, wfxex, std::max(strategy, TryHigherSampleRatesWODefault), true))
  1316 + return true;
  1317 + }
  1318 + }
  1319 + return false;
  1320 +}
  1321 +
  1322 +bool CAESinkWASAPI::FindCompatibleFormatHigherBits(AEAudioFormat format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex, bool tryDefault /*= true*/, bool formaitIsSimplified /*= false*/)
  1323 +{
  1324 + if (!formaitIsSimplified)
  1325 + format = SimplifyFormat(format);
  1326 +
  1327 + int lastIndex;
  1328 + switch (g_advancedSettings.m_WASAPIExclusiveMaximumBits)
  1329 + {
  1330 + case 64: lastIndex = testFormatSequenceIdxDouble; break;
  1331 + case 33: lastIndex = testFormatSequenceIdxFloat; break;
  1332 + case 32: lastIndex = testFormatSequenceIdx32Int; break;
  1333 + case 24: lastIndex = testFormatSequenceIdx24Int; break;
  1334 + case 16: lastIndex = testFormatSequenceIdx16Int; break;
  1335 + default: lastIndex = testFormatSequenceIdx32Int; break;
  1336 + }
  1337 +
  1338 + if (AE_IS_INT(format.m_dataFormat))
  1339 + {
  1340 + /* Requested format is integer format*/
  1341 + int i = 0;
  1342 + /* Find format */
  1343 + do
  1344 + {
  1345 + if (format.m_dataFormat == testFormatSequence[i])
  1346 + break;
  1347 + } while(++i <= lastIndex);
  1348 +
  1349 + if (!tryDefault)
  1350 + i++;
  1351 + /* Try better formats */
  1352 + for(; i <= lastIndex; i++)
  1353 + {
  1354 + format.m_dataFormat = testFormatSequence[i];
  1355 + if (SUCCEEDED(TryAndInitializeExclusive(format, wfxex)))
  1356 + return true;
  1357 + }
  1358 + }
  1359 + else if (format.m_dataFormat == AE_FMT_FLOAT)
  1360 + {
  1361 + if (tryDefault && SUCCEEDED(TryAndInitializeExclusive(format, wfxex)))
  1362 + return true;
  1363 +
  1364 + format.m_dataFormat = AE_FMT_DOUBLE;
  1365 + if (SUCCEEDED(TryAndInitializeExclusive(format, wfxex)))
  1366 + return true;
  1367 + }
  1368 +
  1369 + return false;
  1370 +}
  1371 +
  1372 +bool CAESinkWASAPI::FindCompatibleFormatHigherSampleRate(AEAudioFormat format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex, SearchStrategy strategy, bool formaitIsSimplified /*= false*/)
  1373 +{
  1374 + if (strategy == DefaultOnly || strategy == TryHigherBits)
  1375 + return false;
  1376 + if (format.m_sampleRate < 4000)
  1377 + return false; // Too low start sample rate
  1378 +
  1379 + if (!formaitIsSimplified)
  1380 + format = SimplifyFormat(format);
  1381 +
  1382 + int maxIndex = WASAPITestSampleRatesMaxSafeIndex1;
  1383 + switch(g_advancedSettings.m_WASAPIMaximumPCMSampleRate)
  1384 + {
  1385 + case 384000: maxIndex = WASAPITestSampleRatesMaxIndex; break;
  1386 + case 192000: maxIndex = WASAPITestSampleRatesMaxSafeIndex1; break;
  1387 + case 96000: maxIndex = WASAPITestSampleRatesMaxSafeIndex2; break;
  1388 + }
  1389 +
  1390 + unsigned int startRate = format.m_sampleRate, testRate;
  1391 +
  1392 + for (int i = (strategy != TryHigherSampleRatesWODefault) ? 1 : 2;
  1393 + (testRate = i * startRate) <= WASAPITestSampleRates[maxIndex]; i++)
  1394 + {
  1395 + format.m_sampleRate = testRate;
  1396 + if (strategy == TryHigherSampleRatesOnly)
  1397 + {
  1398 + if (SUCCEEDED(TryAndInitializeExclusive(format, wfxex)))
  1399 + return true;
  1400 + }
  1401 + else
  1402 + {
  1403 + if (FindCompatibleFormatHigherBits(format, wfxex, true, true))
  1404 + return true;
  1405 + }
  1406 + }
  1407 + return false;
  1408 +}
  1409 +
  1410 +bool CAESinkWASAPI::FindCompatibleFormatWithLFE(AEAudioFormat format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex, SearchStrategy strategy, bool formaitIsSimplified /*= false*/)
  1411 +{
  1412 + CAEChannelInfo userSelectedLayout = ((CSoftAE*)CAEFactory::GetEngine())->GetStdChLayout();
  1413 +
  1414 + if (format.m_channelLayout.HasChannel(AE_CH_LFE) || !userSelectedLayout.HasChannel(AE_CH_LFE))
  1415 + return false; // Already with LFE or user don't have LFE
  1416 +
  1417 + if (!formaitIsSimplified)
  1418 + format = SimplifyFormat(format);
  1419 +
  1420 + format.m_channelLayout += AE_CH_LFE;
  1421 +
  1422 + switch (strategy)
  1423 + {
  1424 + case DefaultOnly:
  1425 + return SUCCEEDED(TryAndInitializeExclusive(format, wfxex));
  1426 + break;
  1427 + case TryHigherBits:
  1428 + return FindCompatibleFormatHigherBits(format, wfxex, true, true);
  1429 + break;
  1430 + case TryHigherSampleRates:
  1431 + case TryHigherSampleRatesWODefault:
  1432 + case TryHigherSampleRatesOnly:
  1433 + return FindCompatibleFormatHigherSampleRate(format, wfxex, strategy, true);
  1434 + break;
  1435 + }
  1436 + /* Should be unreachable */
  1437 + return false;
  1438 +}
  1439 +
  1440 +bool CAESinkWASAPI::FindCompatibleFormatSideOrBack(AEAudioFormat format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex, SearchStrategy strategy, bool formaitIsSimplified /*= false*/)
  1441 +{
  1442 + if (!formaitIsSimplified)
  1443 + format = SimplifyFormat(format);
  1444 +
  1445 + if (format.m_channelLayout.HasChannel(AE_CH_SL) && format.m_channelLayout.HasChannel(AE_CH_SR) &&
  1446 + !format.m_channelLayout.HasChannel(AE_CH_BL) && !format.m_channelLayout.HasChannel(AE_CH_BR))
  1447 + {
  1448 + format.m_channelLayout -= AE_CH_SL;
  1449 + format.m_channelLayout -= AE_CH_SR;
  1450 + format.m_channelLayout += AE_CH_BL;
  1451 + format.m_channelLayout += AE_CH_BR;
  1452 +
  1453 + } else if (format.m_channelLayout.HasChannel(AE_CH_BL) && format.m_channelLayout.HasChannel(AE_CH_BR) &&
  1454 + !format.m_channelLayout.HasChannel(AE_CH_SL) && !format.m_channelLayout.HasChannel(AE_CH_SR))
  1455 + {
  1456 + format.m_channelLayout -= AE_CH_BL;
  1457 + format.m_channelLayout -= AE_CH_BR;
  1458 + format.m_channelLayout += AE_CH_SL;
  1459 + format.m_channelLayout += AE_CH_SR;
  1460 + }
  1461 + else
  1462 + return false;
  1463 +
  1464 + switch (strategy)
  1465 + {
  1466 + case DefaultOnly:
  1467 + return SUCCEEDED(TryAndInitializeExclusive(format, wfxex));
  1468 + break;
  1469 + case TryHigherBits:
  1470 + return FindCompatibleFormatHigherBits(format, wfxex, true, true);
  1471 + break;
  1472 + case TryHigherSampleRates:
  1473 + case TryHigherSampleRatesWODefault:
  1474 + case TryHigherSampleRatesOnly:
  1475 + return FindCompatibleFormatHigherSampleRate(format, wfxex, strategy, true);
  1476 + break;
  1477 + }
  1478 + /* Should be unreachable */
  1479 + return false;
  1480 +}
  1481 +
  1482 +bool CAESinkWASAPI::FindCompatibleFormatAnySampleRate(AEAudioFormat format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex, bool checkLowerRates /*= false*/,
  1483 + SearchStrategy strategy /*= TryHigherBits*/, bool formaitIsSimplified /*= false*/)
  1484 +{
  1485 + if (strategy != DefaultOnly && strategy != TryHigherBits)
  1486 + return false;
  1487 + if (!formaitIsSimplified)
  1488 + format = SimplifyFormat(format);
  1489 +
  1490 + int maxIndex = WASAPITestSampleRatesMaxSafeIndex1;
  1491 + switch(g_advancedSettings.m_WASAPIMaximumPCMSampleRate)
  1492 + {
  1493 + case 384000: maxIndex = WASAPITestSampleRatesMaxIndex; break;
  1494 + case 192000: maxIndex = WASAPITestSampleRatesMaxSafeIndex1; break;
  1495 + case 96000: maxIndex = WASAPITestSampleRatesMaxSafeIndex2; break;
  1496 + }
  1497 +
  1498 + if (!checkLowerRates)
  1499 + {
  1500 + int i = 0;
  1501 + while(format.m_sampleRate >= WASAPITestSampleRates[i] && i <= maxIndex)
  1502 + i++;
  1503 + /* Try all standard sample rates that higher requested */
  1504 + for(; i <= maxIndex; i++)
  1505 + {
  1506 + format.m_sampleRate = WASAPITestSampleRates[i];
  1507 + if (FindCompatibleFormatAlmostLossless(format, wfxex, strategy, true))
  1508 + return true;
  1509 + }
  1510 + return false;
  1511 + }
  1512 +
  1513 + int i = maxIndex;
  1514 + while(format.m_sampleRate <= WASAPITestSampleRates[i] && i >= 0)
  1515 + i--;
  1516 + /* Try all standard sample rates that lower requested */
  1517 + for(; i >= 0; i--)
  1518 + {
  1519 + format.m_sampleRate = WASAPITestSampleRates[i];
  1520 + if (FindCompatibleFormatAlmostLossless(format, wfxex, strategy, true))
  1521 + return true;
  1522 + }
  1523 + return false;
1156 1524 }
1157 1525
  1526 +bool CAESinkWASAPI::FindCompatibleFormatAnyDataFormat(AEAudioFormat format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex, bool checkLowerRatesOnly, bool formaitIsSimplified /*= false*/)
  1527 +{
  1528 + if (!formaitIsSimplified)
  1529 + format = SimplifyFormat(format);
  1530 +
  1531 + if (AE_IS_RAW(format.m_dataFormat))
  1532 + return false;
  1533 +
  1534 + int i = SIZEOF_ARRAY (testFormatSequence)-1;
  1535 + while (i > 0 && testFormatSequence[i] != format.m_dataFormat)
  1536 + i--;
  1537 + i--;
  1538 + for(; i > 0; i--)
  1539 + {
  1540 + format.m_dataFormat = testFormatSequence[i];
  1541 + if (!checkLowerRatesOnly && FindCompatibleFormatAlmostLossless(format, wfxex, DefaultOnly, true))
  1542 + return true;
  1543 + if (FindCompatibleFormatAnySampleRate(format, wfxex, checkLowerRatesOnly, DefaultOnly, true))
  1544 + return true;
  1545 + }
  1546 + return false;
  1547 +}
  1548 +
  1549 +
1158 1550 void CAESinkWASAPI::AEChannelsFromSpeakerMask(DWORD speakers)
1159 1551 {
1160 1552 m_channelLayout.Reset();
@@ -1165,8 +1557,182 @@ void CAESinkWASAPI::AEChannelsFromSpeakerMask(DWORD speakers)
1165 1557 m_channelLayout += AEChannelNames[i];
1166 1558 }
1167 1559 }
  1560 +void CAESinkWASAPI::CheckAndCorrectFormat(AEAudioFormat &format)
  1561 +{
  1562 + if (!AE_IS_RAW(format.m_dataFormat))
  1563 + {
  1564 + AEDataFormat maxFormat = AE_FMT_S32NE;
  1565 + switch (g_advancedSettings.m_WASAPIExclusiveMaximumBits)
  1566 + {
  1567 + case 64: maxFormat = AE_FMT_DOUBLE; break;
  1568 + case 33: maxFormat = AE_FMT_FLOAT; break;
  1569 + case 32: maxFormat = AE_FMT_S32NE; break;
  1570 + case 24: maxFormat = AE_FMT_S24NE4; break;
  1571 + case 16: maxFormat = AE_FMT_S16NE; break;
  1572 + }
  1573 +
  1574 + switch (format.m_dataFormat)
  1575 + {
  1576 + case AE_FMT_U8:
  1577 + case AE_FMT_S8:
  1578 + case AE_FMT_S16BE:
  1579 + case AE_FMT_S16LE:
  1580 + case AE_FMT_S16NE: break;
  1581 +
  1582 + case AE_FMT_S32BE:
  1583 + case AE_FMT_S32LE:
  1584 + case AE_FMT_S32NE:
  1585 + if (g_advancedSettings.m_WASAPIExclusiveMaximumBits < 32)
  1586 + format.m_dataFormat = maxFormat;
  1587 + break;
  1588 +
  1589 + case AE_FMT_S24BE4:
  1590 + case AE_FMT_S24LE4:
  1591 + case AE_FMT_S24NE4:
  1592 + case AE_FMT_S24BE3:
  1593 + case AE_FMT_S24LE3:
  1594 + case AE_FMT_S24NE3:
  1595 + if (g_advancedSettings.m_WASAPIExclusiveMaximumBits < 24)
  1596 + format.m_dataFormat = maxFormat;
  1597 + break;
  1598 +
  1599 + case AE_FMT_DOUBLE:
  1600 + if (g_advancedSettings.m_WASAPIExclusiveMaximumBits < 64)
  1601 + format.m_dataFormat = maxFormat;
  1602 + break;
  1603 +
  1604 + case AE_FMT_FLOAT:
  1605 + if (g_advancedSettings.m_WASAPIExclusiveMaximumBits < 33)
  1606 + format.m_dataFormat = maxFormat;