Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

[AE] Fix issues related to endianness in passthrough #973

Merged
merged 6 commits into from Jul 10, 2012
@@ -90,7 +90,9 @@ const char* StreamDescriptionToString(AudioStreamBasicDescription desc, std::str
case kAudioFormat60958AC3:
sstr << "["
<< fourCC
- << "] AC-3/DTS for S/PDIF ("
+ << "] AC-3/DTS for S/PDIF "
+ << ((desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE")
+ << " ("
<< (UInt32)desc.mSampleRate
<< "Hz)";
str = sstr.str();
@@ -27,6 +27,7 @@
#include "utils/log.h"
#include "utils/TimeUtils.h"
#include "utils/MathUtils.h"
+#include "utils/EndianSwap.h"
#include "threads/SingleLock.h"
#include "settings/GUISettings.h"
#include "settings/Settings.h"
@@ -883,6 +884,16 @@ void CSoftAE::Run()
}
}
+void CSoftAE::AllocateConvIfNeeded(size_t convertedSize)
+{
+ if (m_convertedSize < convertedSize)
+ {
+ _aligned_free(m_converted);
+ m_converted = (uint8_t *)_aligned_malloc(convertedSize, 16);
+ m_convertedSize = convertedSize;
+ }
+}
+
void CSoftAE::MixSounds(float *buffer, unsigned int samples)
{
SoundStateList::iterator itt;
@@ -981,12 +992,7 @@ void CSoftAE::RunOutputStage()
if (m_convertFn)
{
const unsigned int convertedBytes = m_sinkFormat.m_frames * m_sinkFormat.m_frameSize;
- if (m_convertedSize < convertedBytes)
- {
- _aligned_free(m_converted);
- m_converted = (uint8_t *)_aligned_malloc(convertedBytes, 16);
- m_convertedSize = convertedBytes;
- }
+ AllocateConvIfNeeded(convertedBytes);
m_convertFn((float*)data, needSamples, m_converted);
data = m_converted;
}
@@ -1000,7 +1006,26 @@ void CSoftAE::RunRawOutputStage()
if(m_buffer.Used() < m_sinkBlockSize)
return;
- int wroteFrames = m_sink->AddPackets((uint8_t*)m_buffer.Raw(m_sinkBlockSize), m_sinkFormat.m_frames);
+ void *data = m_buffer.Raw(m_sinkBlockSize);
+
+ if (CAEUtil::S16NeedsByteSwap(AE_FMT_S16NE, m_sinkFormat.m_dataFormat))
+ {
+ /*
+ * It would really be preferable to handle this at packing stage, so that
+ * it could byteswap the data efficiently without wasting CPU time on
+ * swapping the huge IEC 61937 zero padding between frames (or not
+ * byteswap at all, if there are two byteswaps).
+ *
+ * Unfortunately packing is done on a higher level and we can't easily
+ * tell it the needed format from here, so do it here for now (better than
+ * nothing)...
+ */
+ AllocateConvIfNeeded(m_sinkBlockSize);
+ Endian_Swap16_buf((uint16_t *)m_converted, (uint16_t *)data, m_sinkBlockSize / 2);
+ data = m_converted;
+ }
+
+ int wroteFrames = m_sink->AddPackets((uint8_t *)data, m_sinkFormat.m_frames);
m_buffer.Shift(NULL, wroteFrames * m_sinkFormat.m_frameSize);
}
@@ -192,6 +192,8 @@ class CSoftAE : public IThreadedAE
uint8_t *m_converted;
size_t m_convertedSize;
+ void AllocateConvIfNeeded(size_t convertedSize);
+
/* thread run stages */
void MixSounds (float *buffer, unsigned int samples);
void FinalizeSamples (float *buffer, unsigned int samples);
@@ -193,13 +193,15 @@ bool CAESinkALSA::IsCompatible(const AEAudioFormat format, const std::string dev
snd_pcm_format_t CAESinkALSA::AEFormatToALSAFormat(const enum AEDataFormat format)
{
if (AE_IS_RAW(format))
- return SND_PCM_FORMAT_S16_LE;
+ return SND_PCM_FORMAT_S16;
switch (format)
{
case AE_FMT_S8 : return SND_PCM_FORMAT_S8;
case AE_FMT_U8 : return SND_PCM_FORMAT_U8;
case AE_FMT_S16NE : return SND_PCM_FORMAT_S16;
+ case AE_FMT_S16LE : return SND_PCM_FORMAT_S16_LE;
+ case AE_FMT_S16BE : return SND_PCM_FORMAT_S16_BE;
case AE_FMT_S24NE4: return SND_PCM_FORMAT_S24;
#ifdef __BIG_ENDIAN__
case AE_FMT_S24NE3: return SND_PCM_FORMAT_S24_3BE;
@@ -258,6 +260,10 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format)
{
if (AE_IS_RAW(i) || i == AE_FMT_MAX)
continue;
+
+ if (m_passthrough && i != AE_FMT_S16BE && i != AE_FMT_S16LE)
+ continue;
+
fmt = AEFormatToALSAFormat(i);
if (fmt == SND_PCM_FORMAT_UNKNOWN || snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
@@ -20,6 +20,7 @@
*/
#include "AEBitstreamPacker.h"
+#include "AEPackIEC61937.h"
#include <stdint.h>
#include <stddef.h>
#include <string.h>
@@ -56,6 +57,18 @@ void CAEBitstreamPacker::Pack(CAEStreamInfo &info, uint8_t* data, int size)
PackDTSHD (info, data, size);
break;
+ case CAEStreamInfo::STREAM_TYPE_DTS_512:
+ m_dataSize = CAEPackIEC61937::PackDTS_512(data, size, m_packedBuffer, info.IsLittleEndian());
+ break;
+
+ case CAEStreamInfo::STREAM_TYPE_DTS_1024:
+ m_dataSize = CAEPackIEC61937::PackDTS_1024(data, size, m_packedBuffer, info.IsLittleEndian());
+ break;
+
+ case CAEStreamInfo::STREAM_TYPE_DTS_2048:
+ m_dataSize = CAEPackIEC61937::PackDTS_2048(data, size, m_packedBuffer, info.IsLittleEndian());
+ break;
+
default:
/* pack the data into an IEC61937 frame */
CAEPackIEC61937::PackFunc pack = info.GetPackFunc();
@@ -23,8 +23,6 @@
#include <stdint.h>
#include "../AEAudioFormat.h"
-/* note: always converts to machine byte endian */
-
class CAEConvert{
private:
static unsigned int U8_Float (uint8_t *data, const unsigned int samples, float *dest);
@@ -19,8 +19,6 @@
*
*/
-/* DTS spec shows it suppors both BE and LE, we should not need to convert */
-
#include <cassert>
#include "system.h"
#include "AEPackIEC61937.h"
@@ -85,73 +83,19 @@ int CAEPackIEC61937::PackEAC3(uint8_t *data, unsigned int size, uint8_t *dest)
return OUT_FRAMESTOBYTES(EAC3_FRAME_SIZE);
}
-int CAEPackIEC61937::PackDTS_512(uint8_t *data, unsigned int size, uint8_t *dest)
+int CAEPackIEC61937::PackDTS_512(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian)
{
- assert(size <= OUT_FRAMESTOBYTES(DTS1_FRAME_SIZE));
- struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
- packet->m_preamble1 = IEC61937_PREAMBLE1;
- packet->m_preamble2 = IEC61937_PREAMBLE2;
- packet->m_type = IEC61937_TYPE_DTS1;
- packet->m_length = size << 3;
-
- if (data == NULL)
- data = packet->m_data;
-#ifdef __BIG_ENDIAN__
- else
- memcpy(packet->m_data, data, size);
-#else
- size += size & 0x1;
- SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
-#endif
-
- memset(packet->m_data + size, 0, OUT_FRAMESTOBYTES(DTS1_FRAME_SIZE) - IEC61937_DATA_OFFSET - size);
- return OUT_FRAMESTOBYTES(DTS1_FRAME_SIZE);
+ return PackDTS(data, size, dest, littleEndian, OUT_FRAMESTOBYTES(DTS1_FRAME_SIZE), IEC61937_TYPE_DTS1);
}
-int CAEPackIEC61937::PackDTS_1024(uint8_t *data, unsigned int size, uint8_t *dest)
+int CAEPackIEC61937::PackDTS_1024(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian)
{
- assert(size <= OUT_FRAMESTOBYTES(DTS2_FRAME_SIZE));
- struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
- packet->m_preamble1 = IEC61937_PREAMBLE1;
- packet->m_preamble2 = IEC61937_PREAMBLE2;
- packet->m_type = IEC61937_TYPE_DTS2;
- packet->m_length = size << 3;
-
- if (data == NULL)
- data = packet->m_data;
-#ifdef __BIG_ENDIAN__
- else
- memcpy(packet->m_data, data, size);
-#else
- size += size & 0x1;
- SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
-#endif
-
- memset(packet->m_data + size, 0, OUT_FRAMESTOBYTES(DTS2_FRAME_SIZE) - IEC61937_DATA_OFFSET - size);
- return OUT_FRAMESTOBYTES(DTS2_FRAME_SIZE);
+ return PackDTS(data, size, dest, littleEndian, OUT_FRAMESTOBYTES(DTS2_FRAME_SIZE), IEC61937_TYPE_DTS2);
}
-int CAEPackIEC61937::PackDTS_2048(uint8_t *data, unsigned int size, uint8_t *dest)
+int CAEPackIEC61937::PackDTS_2048(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian)
{
- assert(size <= OUT_FRAMESTOBYTES(DTS3_FRAME_SIZE));
- struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
- packet->m_preamble1 = IEC61937_PREAMBLE1;
- packet->m_preamble2 = IEC61937_PREAMBLE2;
- packet->m_type = IEC61937_TYPE_DTS3;
- packet->m_length = size << 3;
-
- if (data == NULL)
- data = packet->m_data;
-#ifdef __BIG_ENDIAN__
- else
- memcpy(packet->m_data, data, size);
-#else
- size += size & 0x1;
- SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
-#endif
-
- memset(packet->m_data + size, 0, OUT_FRAMESTOBYTES(DTS3_FRAME_SIZE) - IEC61937_DATA_OFFSET - size);
- return OUT_FRAMESTOBYTES(DTS3_FRAME_SIZE);
+ return PackDTS(data, size, dest, littleEndian, OUT_FRAMESTOBYTES(DTS3_FRAME_SIZE), IEC61937_TYPE_DTS3);
}
int CAEPackIEC61937::PackTrueHD(uint8_t *data, unsigned int size, uint8_t *dest)
@@ -220,3 +164,56 @@ int CAEPackIEC61937::PackDTSHD(uint8_t *data, unsigned int size, uint8_t *dest,
return burstsize;
}
+int CAEPackIEC61937::PackDTS(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian,
+ unsigned int frameSize, uint16_t type)
+{
+ assert(size <= frameSize);
+
+ /* BE is the standard endianness, byteswap needed if LE */
+ bool byteSwapNeeded = littleEndian;
+
+#ifndef __BIG_ENDIAN__
+ /* on LE systems we want LE output, byteswap needed */
+ byteSwapNeeded ^= true;
+#endif
+
+ struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
+ uint8_t *dataTo;
+
+ if (size == frameSize)
+ {
+ /* No packing possible or needed, DTS stream is suitable for direct output */
+ dataTo = dest;
+ }
+ else if (size <= frameSize - IEC61937_DATA_OFFSET)
+ {
+ /* Fits to IEC61937, perform packing */
+ packet->m_preamble1 = IEC61937_PREAMBLE1;
+ packet->m_preamble2 = IEC61937_PREAMBLE2;
+ packet->m_type = type;
+ packet->m_length = size << 3;
+
+ dataTo = packet->m_data;
+ }
+ else
+ {
+ /* Stream is unsuitable for both packing and direct output */
+ return 0;
+ }
+
+ if (data == NULL)
+ data = dataTo;
+ else if (!byteSwapNeeded)
+ memcpy(dataTo, data, size);
+
+ if (byteSwapNeeded)
+ {
+ size += size & 0x1;
+ SwapEndian((uint16_t*)dataTo, (uint16_t*)data, size >> 1);
+ }
+
+ if (size != frameSize)
+ memset(packet->m_data + size, 0, frameSize - IEC61937_DATA_OFFSET - size);
+
+ return frameSize;
+}
@@ -52,12 +52,16 @@ class CAEPackIEC61937
static int PackAC3 (uint8_t *data, unsigned int size, uint8_t *dest);
static int PackEAC3 (uint8_t *data, unsigned int size, uint8_t *dest);
- static int PackDTS_512 (uint8_t *data, unsigned int size, uint8_t *dest);
- static int PackDTS_1024(uint8_t *data, unsigned int size, uint8_t *dest);
- static int PackDTS_2048(uint8_t *data, unsigned int size, uint8_t *dest);
+ static int PackDTS_512 (uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian);
+ static int PackDTS_1024(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian);
+ static int PackDTS_2048(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian);
static int PackTrueHD (uint8_t *data, unsigned int size, uint8_t *dest);
static int PackDTSHD (uint8_t *data, unsigned int size, uint8_t *dest, unsigned int period);
private:
+
+ static int PackDTS(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian,
+ unsigned int frameSize, uint16_t type);
+
enum IEC61937DataType
{
IEC61937_TYPE_NULL = 0x00,
@@ -504,9 +504,9 @@ unsigned int CAEStreamInfo::SyncDTS(uint8_t *data, unsigned int size)
DataType dataType;
switch (dtsBlocks << 5)
{
- case 512 : dataType = STREAM_TYPE_DTS_512 ; m_packFunc = &CAEPackIEC61937::PackDTS_512 ; break;
- case 1024: dataType = STREAM_TYPE_DTS_1024; m_packFunc = &CAEPackIEC61937::PackDTS_1024; break;
- case 2048: dataType = STREAM_TYPE_DTS_2048; m_packFunc = &CAEPackIEC61937::PackDTS_2048; break;
+ case 512 : dataType = STREAM_TYPE_DTS_512 ; break;
+ case 1024: dataType = STREAM_TYPE_DTS_1024; break;
+ case 2048: dataType = STREAM_TYPE_DTS_2048; break;
default:
invalid = true;
break;
@@ -455,3 +455,20 @@ void CAEUtil::FloatRand4(const float min, const float max, float result[4], __m1
result[3] = ((float)(m_seed = (214013 * m_seed + 2531011)) * factor) - delta;
#endif
}
+
+bool CAEUtil::S16NeedsByteSwap(AEDataFormat in, AEDataFormat out)
+{
+ const AEDataFormat nativeFormat =
+#ifdef WORDS_BIGENDIAN
+ AE_FMT_S16BE;
+#else
+ AE_FMT_S16LE;
+#endif
+
+ if (in == AE_FMT_S16NE || AE_IS_RAW(in))
+ in = nativeFormat;
+ if (out == AE_FMT_S16NE || AE_IS_RAW(out))
+ out = nativeFormat;
+
+ return in != out;
+}
@elupus

elupus May 18, 2012

Member

This seem overly complicated. Why isn't AE_FMT_S16NE defined to AE_FMT_S16BE or AE_FMT_S16LE based on WORDS_BIGENDIAN format from the start?

@anssih

anssih May 19, 2012

Member

Good question, maybe the AE guys know... @gnif, @DDDamian ?

@DDDamian

DDDamian May 19, 2012

Contributor

Heh - guessing this was AE development day 3, but gnif might have a better answer.

@@ -74,4 +74,6 @@ class CAEUtil
*/
static float FloatRand1(const float min, const float max);
static void FloatRand4(const float min, const float max, float result[4], __m128 *sseresult = NULL);
+
+ static bool S16NeedsByteSwap(AEDataFormat in, AEDataFormat out);
};
Oops, something went wrong.