diff --git a/spa/plugins/audioconvert/fmt-ops.h b/spa/plugins/audioconvert/fmt-ops.h index c8d1d5262..17445a063 100644 --- a/spa/plugins/audioconvert/fmt-ops.h +++ b/spa/plugins/audioconvert/fmt-ops.h @@ -88,6 +88,18 @@ #define U32_TO_U24_32(v) (((uint32_t)(v)) >> 8) +#define S25_MIN -16777216 +#define S25_MAX 16777215 +#define S25_SCALE 16777216.0f +#define S25_32_TO_F32(v) ITOF(int32_t, v, S25_SCALE, 0.0f) +#define S25_32S_TO_F32(v) S25_32_TO_F32(bswap_32(v)) +#define F32_TO_S25_32_D(v,d) FTOI(int32_t, v, S25_SCALE, 0.0f, d, S25_MIN, S25_MAX) +#define F32_TO_S25_32(v) F32_TO_S25_32_D(v, 0.0f) +#define F32_TO_S25_32S(v) bswap_32(F32_TO_S25_32(v)) +#define F32_TO_S25_32S_D(v,d) bswap_32(F32_TO_S25_32_D(v,d)) +#define S25_32_TO_S32(v) ((int32_t)(((uint32_t)(v)) << 7)) +#define S32_TO_S25_32(v) (((int32_t)(v)) >> 7) + #define U32_MIN 0u #define U32_MAX 4294967295u #define U32_SCALE 2147483648.f diff --git a/spa/plugins/audioconvert/test-fmt-ops.c b/spa/plugins/audioconvert/test-fmt-ops.c index b14da3a58..ee5ce988c 100644 --- a/spa/plugins/audioconvert/test-fmt-ops.c +++ b/spa/plugins/audioconvert/test-fmt-ops.c @@ -291,10 +291,22 @@ static void test_u32_f32(void) static void test_f32_s32(void) { static const float in[] = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, - 1.0f/0xa00000, 1.0f/0x1000000, -1.0f/0xa00000, -1.0f/0x1000000 }; - static const int32_t out[] = { 0, 0x7fffff00, 0x80000000, 0x40000000, 0xc0000000, - 0x7fffff00, 0x80000000, - 0x00000100, 0x00000000, 0xffffff00, 0x00000000 }; + 1.0f/0xa00000, -1.0f/0xa00000, 1.0f/0x800000, -1.0f/0x800000, + 1.0f/0x1000000, -1.0f/0x1000000, 1.0f/0x2000000, -1.0f/0x2000000, + 1.0f/0x4000000, -1.0f/0x4000000, 1.0f/0x8000000, -1.0f/0x8000000, + 1.0f/0x10000000, -1.0f/0x10000000, 1.0f/0x20000000, -1.0f/0x20000000, + 1.0f/0x40000000, -1.0f/0x40000000, 1.0f/0x80000000, -1.0f/0x80000000, + 1.0f/0x100000000, -1.0f/0x100000000, 1.0f/0x200000000, -1.0f/0x200000000, + }; + static const int32_t out[] = { 0x00000000, 0x7fffff00, 0x80000000, + 0x40000000, 0xc0000000, 0x7fffff00, 0x80000000, 0x00000100, + 0xffffff00, 0x00000100, 0xffffff00, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + }; + run_test("test_f32_s32", in, sizeof(in[0]), out, sizeof(out[0]), SPA_N_ELEMENTS(out), true, true, conv_f32_to_s32_c); @@ -320,8 +332,15 @@ static void test_f32_s32(void) static void test_s32_f32(void) { - static const int32_t in[] = { 0, 0x7fffff00, 0x80000000, 0x40000000, 0xc0000000 }; - static const float out[] = { 0.0f, 0.999999880791f, -1.0f, 0.5, -0.5, }; + static const int32_t in[] = { 0, 0x7FFFFFFF, 0x80000000, 0x7fffff00, + 0x80000100, 0x40000000, 0xc0000000, 0x0080, 0xFFFFFF80, 0x0100, + 0xFFFFFF00, 0x0200, 0xFFFFFE00 + }; + static const float out[] = { 0.e+00f, 9.9999988079071044921875e-01f, -1.e+00f, + 9.9999988079071044921875e-01f, -9.9999988079071044921875e-01f, 5.e-01f, + -5.e-01f, 0.e+00f, -1.1920928955078125e-07f, 1.1920928955078125e-07f, + -1.1920928955078125e-07f, 2.384185791015625e-07f, -2.384185791015625e-07f + }; run_test("test_s32_f32d", in, sizeof(in[0]), out, sizeof(out[0]), SPA_N_ELEMENTS(out), true, false, conv_s32_to_f32d_c); @@ -600,16 +619,102 @@ static void test_lossless_u24(void) } } -static void test_lossless_s32(void) +static void test_lossless_s25_32_to_f32_to_s25_32(void) { int32_t i; fprintf(stderr, "test %s:\n", __func__); - for (i = S32_MIN; i < S32_MAX; i+=255) { + for (i = S25_MIN; i <= S25_MAX; i+=1) { + float v = S25_32_TO_F32(i); + int32_t t = F32_TO_S25_32(v); + spa_assert_se(i == t); + } +} + +static void test_lossless_s25_32_to_s32_to_f32_to_s25_32_XFAIL(void) +{ + int32_t i; + + int all_lossless = 1; + int32_t max_abs_err = -1; + fprintf(stderr, "test %s:\n", __func__); + for (i = S25_MIN; i <= S25_MAX; i+=1) { + float v = S32_TO_F32(S25_32_TO_S32(i)); + int32_t t = F32_TO_S25_32(v); + all_lossless &= i == t; + max_abs_err = SPA_MAX(max_abs_err, SPA_ABS(i - t)); + } + spa_assert_se(!all_lossless); + spa_assert_se(max_abs_err == 1); +} + +static void test_lossless_s25_32_to_s32_to_f32_to_s32_to_s25_32_XFAIL(void) +{ + int32_t i; + + int all_lossless = 1; + int32_t max_abs_err = -1; + fprintf(stderr, "test %s:\n", __func__); + for (i = S25_MIN; i <= S25_MAX; i+=1) { + float v = S32_TO_F32(S25_32_TO_S32(i)); + int32_t t = S32_TO_S25_32(F32_TO_S32(v)); + all_lossless &= i == t; + max_abs_err = SPA_MAX(max_abs_err, SPA_ABS(i - t)); + } + spa_assert_se(!all_lossless); + spa_assert_se(max_abs_err == 1); +} + +static void test_lossless_s25_32_to_f32_to_s32_to_s25_32_XFAIL(void) +{ + int32_t i; + + int all_lossless = 1; + int32_t max_abs_err = -1; + fprintf(stderr, "test %s:\n", __func__); + for (i = S25_MIN; i <= S25_MAX; i+=1) { + float v = S25_32_TO_F32(i); + int32_t t = S32_TO_S25_32(F32_TO_S32(v)); + all_lossless &= i == t; + max_abs_err = SPA_MAX(max_abs_err, SPA_ABS(i - t)); + } + spa_assert_se(!all_lossless); + spa_assert_se(max_abs_err == 1); +} + +static void test_lossless_s32(void) +{ + int64_t i; + + int32_t max_abs_err = -1; + const int32_t expected_max_abs_err = 255; + fprintf(stderr, "test %s:\n", __func__); + for (i = S32_MIN; i < S32_MAX; i += (expected_max_abs_err >> 1)) { float v = S32_TO_F32(i); int32_t t = F32_TO_S32(v); - spa_assert_se(SPA_ABS(i - t) <= 256); + max_abs_err = SPA_MAX(max_abs_err, SPA_ABS(i - t)); } + spa_assert_se(max_abs_err == expected_max_abs_err); +} + +static void test_lossless_s32_lossless_subset(void) +{ + int32_t i, j; + + int all_lossless = 1; + int32_t max_abs_err = -1; + fprintf(stderr, "test %s:\n", __func__); + for (i = S25_MIN; i <= S25_MAX; i+=1) { + for(j = 0; j < 8; ++j) { + int32_t s = i * (1<