|
@@ -36,7 +36,7 @@
|
|
|
#endif
|
|
|
|
|
|
#if HAVE_SSE3_INTRINSICS
|
|
|
-/* Effectively mix right and left channels into a single channel */
|
|
|
+/* Convert from stereo to mono. Average left and right. */
|
|
|
static void SDLCALL
|
|
|
SDL_ConvertStereoToMono_SSE3(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
{
|
|
@@ -71,7 +71,7 @@ SDL_ConvertStereoToMono_SSE3(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-/* Effectively mix right and left channels into a single channel */
|
|
|
+/* Convert from stereo to mono. Average left and right. */
|
|
|
static void SDLCALL
|
|
|
SDL_ConvertStereoToMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
{
|
|
@@ -93,7 +93,7 @@ SDL_ConvertStereoToMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
}
|
|
|
|
|
|
|
|
|
-/* Convert from 5.1 to stereo. Average left and right, discard subwoofer. */
|
|
|
+/* Convert from 5.1 to stereo. Average left and right, distribute center, discard LFE. */
|
|
|
static void SDLCALL
|
|
|
SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
{
|
|
@@ -104,11 +104,11 @@ SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
LOG_DEBUG_CONVERT("5.1", "stereo");
|
|
|
SDL_assert(format == AUDIO_F32SYS);
|
|
|
|
|
|
- /* this assumes FL+FR+FC+subwoof+BL+BR layout. */
|
|
|
+ /* SDL's 5.1 layout: FL+FR+FC+LFE+BL+BR */
|
|
|
for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
|
|
|
- const double front_center = (double) src[2];
|
|
|
- dst[0] = (float) ((src[0] + front_center + src[4]) / 3.0); /* left */
|
|
|
- dst[1] = (float) ((src[1] + front_center + src[5]) / 3.0); /* right */
|
|
|
+ const float front_center_distributed = src[2] * 0.5f;
|
|
|
+ dst[0] = (src[0] + front_center_distributed + src[4]) / 2.5f; /* left */
|
|
|
+ dst[1] = (src[1] + front_center_distributed + src[5]) / 2.5f; /* right */
|
|
|
}
|
|
|
|
|
|
cvt->len_cvt /= 3;
|
|
@@ -118,7 +118,60 @@ SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
}
|
|
|
|
|
|
|
|
|
-/* Convert from 5.1 to quad */
|
|
|
+/* Convert from quad to stereo. Average left and right. */
|
|
|
+static void SDLCALL
|
|
|
+SDL_ConvertQuadToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
+{
|
|
|
+ float *dst = (float *) cvt->buf;
|
|
|
+ const float *src = dst;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ LOG_DEBUG_CONVERT("quad", "stereo");
|
|
|
+ SDL_assert(format == AUDIO_F32SYS);
|
|
|
+
|
|
|
+ for (i = cvt->len_cvt / (sizeof (float) * 4); i; --i, src += 4, dst += 2) {
|
|
|
+ dst[0] = (src[0] + src[2]) * 0.5f; /* left */
|
|
|
+ dst[1] = (src[1] + src[3]) * 0.5f; /* right */
|
|
|
+ }
|
|
|
+
|
|
|
+ cvt->len_cvt /= 3;
|
|
|
+ if (cvt->filters[++cvt->filter_index]) {
|
|
|
+ cvt->filters[cvt->filter_index] (cvt, format);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* Convert from 7.1 to 5.1. Distribute sides across front and back. */
|
|
|
+static void SDLCALL
|
|
|
+SDL_Convert71To51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
+{
|
|
|
+ float *dst = (float *) cvt->buf;
|
|
|
+ const float *src = dst;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ LOG_DEBUG_CONVERT("7.1", "5.1");
|
|
|
+ SDL_assert(format == AUDIO_F32SYS);
|
|
|
+
|
|
|
+ for (i = cvt->len_cvt / (sizeof (float) * 8); i; --i, src += 8, dst += 6) {
|
|
|
+ const float surround_left_distributed = src[6] * 0.5f;
|
|
|
+ const float surround_right_distributed = src[7] * 0.5f;
|
|
|
+ dst[0] = (src[0] + surround_left_distributed) / 1.5f; /* FL */
|
|
|
+ dst[1] = (src[1] + surround_right_distributed) / 1.5f; /* FR */
|
|
|
+ dst[2] = src[2] / 1.5f; /* CC */
|
|
|
+ dst[3] = src[3] / 1.5f; /* LFE */
|
|
|
+ dst[4] = (src[4] + surround_left_distributed) / 1.5f; /* BL */
|
|
|
+ dst[5] = (src[5] + surround_right_distributed) / 1.5f; /* BR */
|
|
|
+ }
|
|
|
+
|
|
|
+ cvt->len_cvt /= 8;
|
|
|
+ cvt->len_cvt *= 6;
|
|
|
+ if (cvt->filters[++cvt->filter_index]) {
|
|
|
+ cvt->filters[cvt->filter_index] (cvt, format);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* Convert from 5.1 to quad. Distribute center across front, discard LFE. */
|
|
|
static void SDLCALL
|
|
|
SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
{
|
|
@@ -129,14 +182,14 @@ SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
LOG_DEBUG_CONVERT("5.1", "quad");
|
|
|
SDL_assert(format == AUDIO_F32SYS);
|
|
|
|
|
|
- /* assumes quad is FL+FR+BL+BR layout and 5.1 is FL+FR+FC+subwoof+BL+BR */
|
|
|
+ /* SDL's 4.0 layout: FL+FR+BL+BR */
|
|
|
+ /* SDL's 5.1 layout: FL+FR+FC+LFE+BL+BR */
|
|
|
for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
|
|
|
- /* FIXME: this is a good candidate for SIMD. */
|
|
|
- const double front_center = (double) src[2];
|
|
|
- dst[0] = (float) ((src[0] + front_center) * 0.5); /* FL */
|
|
|
- dst[1] = (float) ((src[1] + front_center) * 0.5); /* FR */
|
|
|
- dst[2] = (float) ((src[4] + front_center) * 0.5); /* BL */
|
|
|
- dst[3] = (float) ((src[5] + front_center) * 0.5); /* BR */
|
|
|
+ const float front_center_distributed = src[2] * 0.5f;
|
|
|
+ dst[0] = (src[0] + front_center_distributed) / 1.5f; /* FL */
|
|
|
+ dst[1] = (src[1] + front_center_distributed) / 1.5f; /* FR */
|
|
|
+ dst[2] = src[4] / 1.5f; /* BL */
|
|
|
+ dst[3] = src[5] / 1.5f; /* BR */
|
|
|
}
|
|
|
|
|
|
cvt->len_cvt /= 6;
|
|
@@ -147,7 +200,7 @@ SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
}
|
|
|
|
|
|
|
|
|
-/* Duplicate a mono channel to both stereo channels */
|
|
|
+/* Upmix mono to stereo (by duplication) */
|
|
|
static void SDLCALL
|
|
|
SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
{
|
|
@@ -171,7 +224,7 @@ SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
}
|
|
|
|
|
|
|
|
|
-/* Duplicate a stereo channel to a pseudo-5.1 stream */
|
|
|
+/* Upmix stereo to a pseudo-5.1 stream */
|
|
|
static void SDLCALL
|
|
|
SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
{
|
|
@@ -183,16 +236,17 @@ SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
LOG_DEBUG_CONVERT("stereo", "5.1");
|
|
|
SDL_assert(format == AUDIO_F32SYS);
|
|
|
|
|
|
- for (i = cvt->len_cvt / 8; i; --i) {
|
|
|
+ for (i = cvt->len_cvt / (sizeof(float) * 2); i; --i) {
|
|
|
dst -= 6;
|
|
|
src -= 2;
|
|
|
lf = src[0];
|
|
|
rf = src[1];
|
|
|
ce = (lf + rf) * 0.5f;
|
|
|
+ /* !!! FIXME: FL and FR may clip */
|
|
|
dst[0] = lf + (lf - ce); /* FL */
|
|
|
dst[1] = rf + (rf - ce); /* FR */
|
|
|
dst[2] = ce; /* FC */
|
|
|
- dst[3] = ce; /* !!! FIXME: wrong! This is the subwoofer. */
|
|
|
+ dst[3] = 0; /* LFE (only meant for special LFE effects) */
|
|
|
dst[4] = lf; /* BL */
|
|
|
dst[5] = rf; /* BR */
|
|
|
}
|
|
@@ -204,7 +258,44 @@ SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
}
|
|
|
|
|
|
|
|
|
-/* Duplicate a stereo channel to a pseudo-4.0 stream */
|
|
|
+/* Upmix quad to a pseudo-5.1 stream */
|
|
|
+static void SDLCALL
|
|
|
+SDL_ConvertQuadTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ float lf, rf, lb, rb, ce;
|
|
|
+ const float *src = (const float *) (cvt->buf + cvt->len_cvt);
|
|
|
+ float *dst = (float *) (cvt->buf + cvt->len_cvt * 3 / 2);
|
|
|
+
|
|
|
+ LOG_DEBUG_CONVERT("quad", "5.1");
|
|
|
+ SDL_assert(format == AUDIO_F32SYS);
|
|
|
+ SDL_assert(cvt->len_cvt % (sizeof(float) * 4) == 0);
|
|
|
+
|
|
|
+ for (i = cvt->len_cvt / (sizeof(float) * 4); i; --i) {
|
|
|
+ dst -= 6;
|
|
|
+ src -= 4;
|
|
|
+ lf = src[0];
|
|
|
+ rf = src[1];
|
|
|
+ lb = src[2];
|
|
|
+ rb = src[3];
|
|
|
+ ce = (lf + rf) * 0.5f;
|
|
|
+ /* !!! FIXME: FL and FR may clip */
|
|
|
+ dst[0] = lf + (lf - ce); /* FL */
|
|
|
+ dst[1] = rf + (rf - ce); /* FR */
|
|
|
+ dst[2] = ce; /* FC */
|
|
|
+ dst[3] = 0; /* LFE (only meant for special LFE effects) */
|
|
|
+ dst[4] = lb; /* BL */
|
|
|
+ dst[5] = rb; /* BR */
|
|
|
+ }
|
|
|
+
|
|
|
+ cvt->len_cvt = cvt->len_cvt * 3 / 2;
|
|
|
+ if (cvt->filters[++cvt->filter_index]) {
|
|
|
+ cvt->filters[cvt->filter_index] (cvt, format);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* Upmix stereo to a pseudo-4.0 stream (by duplication) */
|
|
|
static void SDLCALL
|
|
|
SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
{
|
|
@@ -216,7 +307,7 @@ SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
LOG_DEBUG_CONVERT("stereo", "quad");
|
|
|
SDL_assert(format == AUDIO_F32SYS);
|
|
|
|
|
|
- for (i = cvt->len_cvt / 8; i; --i) {
|
|
|
+ for (i = cvt->len_cvt / (sizeof(float) * 2); i; --i) {
|
|
|
dst -= 4;
|
|
|
src -= 2;
|
|
|
lf = src[0];
|
|
@@ -233,6 +324,52 @@ SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/* Upmix 5.1 to 7.1 */
|
|
|
+static void SDLCALL
|
|
|
+SDL_Convert51To71(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|
|
+{
|
|
|
+ float lf, rf, lb, rb, ls, rs;
|
|
|
+ int i;
|
|
|
+ const float *src = (const float *) (cvt->buf + cvt->len_cvt);
|
|
|
+ float *dst = (float *) (cvt->buf + cvt->len_cvt * 4 / 3);
|
|
|
+
|
|
|
+ LOG_DEBUG_CONVERT("5.1", "7.1");
|
|
|
+ SDL_assert(format == AUDIO_F32SYS);
|
|
|
+ SDL_assert(cvt->len_cvt % (sizeof(float) * 6) == 0);
|
|
|
+
|
|
|
+ for (i = cvt->len_cvt / (sizeof(float) * 6); i; --i) {
|
|
|
+ dst -= 8;
|
|
|
+ src -= 6;
|
|
|
+ lf = src[0];
|
|
|
+ rf = src[1];
|
|
|
+ lb = src[4];
|
|
|
+ rb = src[5];
|
|
|
+ ls = (lf + lb) * 0.5f;
|
|
|
+ rs = (rf + rb) * 0.5f;
|
|
|
+ /* !!! FIXME: these four may clip */
|
|
|
+ lf += lf - ls;
|
|
|
+ rf += rf - ls;
|
|
|
+ lb += lb - ls;
|
|
|
+ rb += rb - ls;
|
|
|
+ dst[3] = src[3]; /* LFE */
|
|
|
+ dst[2] = src[2]; /* FC */
|
|
|
+ dst[7] = rs; /* SR */
|
|
|
+ dst[6] = ls; /* SL */
|
|
|
+ dst[5] = rb; /* BR */
|
|
|
+ dst[4] = lb; /* BL */
|
|
|
+ dst[1] = rf; /* FR */
|
|
|
+ dst[0] = lf; /* FL */
|
|
|
+ }
|
|
|
+
|
|
|
+ cvt->len_cvt = cvt->len_cvt * 4 / 3;
|
|
|
+
|
|
|
+ if (cvt->filters[++cvt->filter_index]) {
|
|
|
+ cvt->filters[cvt->filter_index] (cvt, format);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int
|
|
|
SDL_ResampleAudioSimple(const int chans, const double rate_incr,
|
|
|
float *last_sample, const float *inbuf,
|
|
@@ -732,9 +869,9 @@ SDL_SupportedChannelCount(const int channels)
|
|
|
case 2: /* stereo */
|
|
|
case 4: /* quad */
|
|
|
case 6: /* 5.1 */
|
|
|
- return SDL_TRUE; /* supported. */
|
|
|
+ case 8: /* 7.1 */
|
|
|
+ return SDL_TRUE; /* supported. */
|
|
|
|
|
|
- case 8: /* !!! FIXME: 7.1 */
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
@@ -857,7 +994,9 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
|
|
|
}
|
|
|
|
|
|
/* Channel conversion */
|
|
|
- if (src_channels != dst_channels) {
|
|
|
+ if (src_channels < dst_channels) {
|
|
|
+ /* Upmixing */
|
|
|
+ /* Mono -> Stereo [-> ...] */
|
|
|
if ((src_channels == 1) && (dst_channels > 1)) {
|
|
|
if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertMonoToStereo) < 0) {
|
|
|
return -1;
|
|
@@ -866,7 +1005,8 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
|
|
|
src_channels = 2;
|
|
|
cvt->len_ratio *= 2;
|
|
|
}
|
|
|
- if ((src_channels == 2) && (dst_channels == 6)) {
|
|
|
+ /* [Mono ->] Stereo -> 5.1 [-> 7.1] */
|
|
|
+ if ((src_channels == 2) && (dst_channels >= 6)) {
|
|
|
if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoTo51) < 0) {
|
|
|
return -1;
|
|
|
}
|
|
@@ -874,6 +1014,27 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
|
|
|
cvt->len_mult *= 3;
|
|
|
cvt->len_ratio *= 3;
|
|
|
}
|
|
|
+ /* Quad -> 5.1 [-> 7.1] */
|
|
|
+ if ((src_channels == 4) && (dst_channels >= 6)) {
|
|
|
+ if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertQuadTo51) < 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ src_channels = 6;
|
|
|
+ cvt->len_mult = (cvt->len_mult * 3 + 1) / 2;
|
|
|
+ cvt->len_ratio *= 1.5;
|
|
|
+ }
|
|
|
+ /* [[Mono ->] Stereo ->] 5.1 -> 7.1 */
|
|
|
+ if ((src_channels == 6) && (dst_channels == 8)) {
|
|
|
+ if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51To71) < 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ src_channels = 8;
|
|
|
+ cvt->len_mult = (cvt->len_mult * 4 + 2) / 3;
|
|
|
+ /* Should be numerically exact with every valid input to this
|
|
|
+ function */
|
|
|
+ cvt->len_ratio = cvt->len_ratio * 4 / 3;
|
|
|
+ }
|
|
|
+ /* [Mono ->] Stereo -> Quad */
|
|
|
if ((src_channels == 2) && (dst_channels == 4)) {
|
|
|
if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoToQuad) < 0) {
|
|
|
return -1;
|
|
@@ -882,14 +1043,18 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
|
|
|
cvt->len_mult *= 2;
|
|
|
cvt->len_ratio *= 2;
|
|
|
}
|
|
|
- while ((src_channels * 2) <= dst_channels) {
|
|
|
- if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertMonoToStereo) < 0) {
|
|
|
+ } else if (src_channels > dst_channels) {
|
|
|
+ /* Downmixing */
|
|
|
+ /* 7.1 -> 5.1 [-> Stereo [-> Mono]] */
|
|
|
+ /* 7.1 -> 5.1 [-> Quad] */
|
|
|
+ if ((src_channels == 8) && (dst_channels <= 6)) {
|
|
|
+ if (SDL_AddAudioCVTFilter(cvt, SDL_Convert71To51) < 0) {
|
|
|
return -1;
|
|
|
}
|
|
|
- cvt->len_mult *= 2;
|
|
|
- src_channels *= 2;
|
|
|
- cvt->len_ratio *= 2;
|
|
|
+ src_channels = 6;
|
|
|
+ cvt->len_ratio *= 0.75;
|
|
|
}
|
|
|
+ /* [7.1 ->] 5.1 -> Stereo [-> Mono] */
|
|
|
if ((src_channels == 6) && (dst_channels <= 2)) {
|
|
|
if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToStereo) < 0) {
|
|
|
return -1;
|
|
@@ -897,19 +1062,24 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
|
|
|
src_channels = 2;
|
|
|
cvt->len_ratio /= 3;
|
|
|
}
|
|
|
+ /* 5.1 -> Quad */
|
|
|
if ((src_channels == 6) && (dst_channels == 4)) {
|
|
|
if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToQuad) < 0) {
|
|
|
return -1;
|
|
|
}
|
|
|
src_channels = 4;
|
|
|
+ cvt->len_ratio = cvt->len_ratio * 2 / 3;
|
|
|
+ }
|
|
|
+ /* Quad -> Stereo [-> Mono] */
|
|
|
+ if ((src_channels == 4) && (dst_channels <= 2)) {
|
|
|
+ if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertQuadToStereo) < 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ src_channels = 2;
|
|
|
cvt->len_ratio /= 2;
|
|
|
}
|
|
|
- /* This assumes that 4 channel audio is in the format:
|
|
|
- Left {front/back} + Right {front/back}
|
|
|
- so converting to L/R stereo works properly.
|
|
|
- */
|
|
|
- while (((src_channels % 2) == 0) &&
|
|
|
- ((src_channels / 2) >= dst_channels)) {
|
|
|
+ /* [... ->] Stereo -> Mono */
|
|
|
+ if ((src_channels == 2) && (dst_channels == 1)) {
|
|
|
SDL_AudioFilter filter = NULL;
|
|
|
|
|
|
#if HAVE_SSE3_INTRINSICS
|
|
@@ -926,14 +1096,17 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
- src_channels /= 2;
|
|
|
+ src_channels = 1;
|
|
|
cvt->len_ratio /= 2;
|
|
|
}
|
|
|
- if (src_channels != dst_channels) {
|
|
|
- /* Uh oh.. */ ;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
+ if (src_channels != dst_channels) {
|
|
|
+ /* All combinations of supported channel counts should have been
|
|
|
+ handled by now, but let's be defensive */
|
|
|
+ return SDL_SetError("Invalid channel combination");
|
|
|
+ }
|
|
|
+
|
|
|
/* Do rate conversion, if necessary. Updates (cvt). */
|
|
|
if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
|
|
|
return -1; /* shouldn't happen, but just in case... */
|