OSDN Git Service

Add q4_27 conversion from float with tests
authorAndy Hung <hunga@google.com>
Fri, 4 Apr 2014 20:05:43 +0000 (13:05 -0700)
committerAndy Hung <hunga@google.com>
Fri, 4 Apr 2014 22:15:05 +0000 (15:15 -0700)
Change-Id: If7cb0b1e1fbd34202a4be69e91cb2c8743ab0ad6
Signed-off-by: Andy Hung <hunga@google.com>
audio_utils/include/audio_utils/primitives.h
audio_utils/primitives.c
audio_utils/tests/primitives_tests.cpp

index 8f36139..26dc63d 100644 (file)
@@ -103,7 +103,7 @@ void memcpy_to_i16_from_float(int16_t *dst, const float *src, size_t count);
  * The destination and source buffers must either be completely separate (non-overlapping), or
  * they must both start at the same address.  Partially overlapping buffers are not supported.
  */
-void memcpy_to_float_from_q4_27(float *dst, const int32_t *src, size_t c);
+void memcpy_to_float_from_q4_27(float *dst, const int32_t *src, size_t count);
 
 /* Copy samples from signed fixed-point 16 bit Q0.15 to single-precision floating-point.
  * The output float range is [-1.0, 1.0) for the fixed-point range [0x8000, 0x7fff].
@@ -188,6 +188,19 @@ void memcpy_to_q8_23_from_i16(int32_t *dst, const int16_t *src, size_t count);
  */
 void memcpy_to_q8_23_from_float_with_clamp(int32_t *dst, const float *src, size_t count);
 
+/* Copy samples from single-precision floating-point to signed fixed-point 32-bit Q4.27.
+ * The conversion will use the full available Q4.27 range, including guard bits.
+ * Fractional lsb is rounded to nearest, ties away from zero.
+ * See clampq4_27_from_float() for details.
+ * Parameters:
+ *  dst     Destination buffer
+ *  src     Source buffer
+ *  count   Number of samples to copy
+ * The destination and source buffers must either be completely separate (non-overlapping), or
+ * they must both start at the same address.  Partially overlapping buffers are not supported.
+ */
+void memcpy_to_q4_27_from_float(int32_t *dst, const float *src, size_t count);
+
 /* Copy samples from signed fixed-point 32-bit Q8.23 to signed fixed point 16-bit Q0.15.
  * The data is clamped, and truncated without rounding.
  * Parameters:
@@ -359,6 +372,31 @@ static inline int32_t clamp24_from_float(float f)
     return f > 0 ? f + 0.5 : f - 0.5;
 }
 
+/* Convert a single-precision floating point value to a Q4.27 integer value.
+ * Rounds to nearest, ties away from 0.
+ *
+ * Values outside the range [-16.0, 16.0) are properly clamped to -2147483648 and 2147483647,
+ * including -Inf and +Inf. NaN values are considered undefined, and behavior may change
+ * depending on hardware and future implementation of this function.
+ */
+static inline int32_t clampq4_27_from_float(float f)
+{
+    static const float scale = (float)(1UL << 27);
+    static const float limpos = 16.;
+    static const float limneg = -16.;
+
+    if (f <= limneg) {
+        return -0x80000000; /* or 0x80000000 */
+    } else if (f >= limpos) {
+        return 0x7fffffff;
+    }
+    f *= scale;
+    /* integer conversion is through truncation (though int to float is not).
+     * ensure that we round to nearest, ties away from 0.
+     */
+    return f > 0 ? f + 0.5 : f - 0.5;
+}
+
 /* Convert a single-precision floating point value to a Q0.31 integer value.
  * Rounds to nearest, ties away from 0.
  *
@@ -373,7 +411,7 @@ static inline int32_t clamp32_from_float(float f)
     static const float limneg = -1.;
 
     if (f <= limneg) {
-        return -0x80000000;
+        return -0x80000000; /* or 0x80000000 */
     } else if (f >= limpos) {
         return 0x7fffffff;
     }
index 4553688..443ed9c 100644 (file)
@@ -60,10 +60,9 @@ void memcpy_to_i16_from_float(int16_t *dst, const float *src, size_t count)
     }
 }
 
-void memcpy_to_float_from_q4_27(float *dst, const int32_t *src, size_t c)
+void memcpy_to_float_from_q4_27(float *dst, const int32_t *src, size_t count)
 {
-    size_t i;
-    for (i = 0; i < c; ++i) {
+    while (count--) {
         *dst++ = float_from_q4_27(*src++);
     }
 }
@@ -141,6 +140,13 @@ void memcpy_to_q8_23_from_float_with_clamp(int32_t *dst, const float *src, size_
     }
 }
 
+void memcpy_to_q4_27_from_float(int32_t *dst, const float *src, size_t count)
+{
+    while (count--) {
+        *dst++ = clampq4_27_from_float(*src++);
+    }
+}
+
 void memcpy_to_i16_from_q8_23(int16_t *dst, const int32_t *src, size_t count)
 {
     while (count--) {
index 4a51cbc..758a990 100644 (file)
@@ -35,7 +35,7 @@ inline void testClamp16(float f)
     int16_t ival = clamp16_from_float(f / (1 << 15));
 
     // test clamping
-    ALOGV("clamp16(%f) = %d\n", f, ival);
+    ALOGV("clamp16_from_float(%f) = %d\n", f, ival);
     if (f > lim16pos) {
         EXPECT_EQ(ival, lim16pos);
     } else if (f < lim16neg) {
@@ -55,7 +55,7 @@ inline void testClamp24(float f)
     int32_t ival = clamp24_from_float(f / (1 << 23));
 
     // test clamping
-    ALOGV("clamp24(%f) = %d\n", f, ival);
+    ALOGV("clamp24_from_float(%f) = %d\n", f, ival);
     if (f > lim24pos) {
         EXPECT_EQ(ival, lim24pos);
     } else if (f < lim24neg) {
@@ -70,7 +70,15 @@ inline void testClamp24(float f)
     }
 }
 
-TEST(audio_utils_primitives, clamp) {
+template<typename T>
+void checkMonotone(const T *ary, size_t size)
+{
+    for (size_t i = 1; i < size; ++i) {
+        EXPECT_LT(ary[i-1], ary[i]);
+    }
+}
+
+TEST(audio_utils_primitives, clamp_to_int) {
     static const float testArray[] = {
             -NAN, -INFINITY,
             -1.e20, -32768., 63.9,
@@ -85,29 +93,51 @@ TEST(audio_utils_primitives, clamp) {
     for (size_t i = 0; i < ARRAY_SIZE(testArray); ++i) {
         testClamp24(testArray[i]);
     }
+
+    // used for ULP testing (tweaking the lsb of the float)
+    union {
+        int32_t i;
+        float f;
+    } val;
+    int32_t res;
+
+    // check clampq4_27_from_float()
+    val.f = 16.;
+    res = clampq4_27_from_float(val.f);
+    EXPECT_EQ(res, 0x7fffffff);
+    val.i--;
+    res = clampq4_27_from_float(val.f);
+    EXPECT_LE(res, 0x7fffffff);
+    EXPECT_GE(res, 0x7fff0000);
+    val.f = -16.;
+    res = clampq4_27_from_float(val.f);
+    EXPECT_EQ(res, (int32_t)0x80000000); // negative
+    val.i++;
+    res = clampq4_27_from_float(val.f);
+    EXPECT_GE(res, (int32_t)0x80000000); // negative
+    EXPECT_LE(res, (int32_t)0x80008000); // negative
 }
 
 TEST(audio_utils_primitives, memcpy) {
     // test round-trip.
+    int16_t *i16ref = new int16_t[65536];
     int16_t *i16ary = new int16_t[65536];
     int32_t *i32ary = new int32_t[65536];
     float *fary = new float[65536];
     uint8_t *pary = new uint8_t[65536*3];
 
     for (size_t i = 0; i < 65536; ++i) {
-        i16ary[i] = i; // (caution) overflow to neg.
+        i16ref[i] = i16ary[i] = i - 32768;
     }
 
     // do round-trip testing i16 and float
     memcpy_to_float_from_i16(fary, i16ary, 65536);
     memset(i16ary, 0, 65536 * sizeof(i16ary[0]));
+    checkMonotone(fary, 65536);
 
     memcpy_to_i16_from_float(i16ary, fary, 65536);
     memset(fary, 0, 65536 * sizeof(fary[0]));
-
-    for (size_t i = 0; i < 65536; ++i) {
-        EXPECT_EQ(((unsigned)i16ary[i] & 0xffff), (unsigned)i);
-    }
+    checkMonotone(i16ary, 65536);
 
     // TODO make a template case for the following?
 
@@ -126,51 +156,65 @@ TEST(audio_utils_primitives, memcpy) {
 
     memcpy_to_float_from_p24(fary, pary, 65536);
     memset(pary, 0, 65536 * 3 * sizeof(pary[0]));
+    checkMonotone(fary, 65536);
 
     memcpy_to_p24_from_float(pary, fary, 65536);
     memset(fary, 0, 65536 * sizeof(fary[0]));
 
     memcpy_to_i16_from_p24(i16ary, pary, 65536);
     memset(pary, 0, 65536 * 3 * sizeof(pary[0]));
-
-    for (size_t i = 0; i < 65536; ++i) {
-        EXPECT_EQ(((unsigned)i16ary[i] & 0xffff), (unsigned)i);
-    }
+    checkMonotone(i16ary, 65536);
 
     // do round-trip testing q8_23 to i16 and float
     memcpy_to_q8_23_from_i16(i32ary, i16ary, 65536);
     memset(i16ary, 0, 65536 * sizeof(i16ary[0]));
+    checkMonotone(i32ary, 65536);
 
     memcpy_to_float_from_q8_23(fary, i32ary, 65536);
     memset(i32ary, 0, 65536 * sizeof(i32ary[0]));
+    checkMonotone(fary, 65536);
 
     memcpy_to_q8_23_from_float_with_clamp(i32ary, fary, 65536);
     memset(fary, 0, 65536 * sizeof(fary[0]));
+    checkMonotone(i32ary, 65536);
 
     memcpy_to_i16_from_q8_23(i16ary, i32ary, 65536);
     memset(i32ary, 0, 65536 * sizeof(i32ary[0]));
-
-    for (size_t i = 0; i < 65536; ++i) {
-        EXPECT_EQ(((unsigned)i16ary[i] & 0xffff), (unsigned)i);
-    }
+    checkMonotone(i16ary, 65536);
 
     // do round-trip testing i32 to i16 and float
     memcpy_to_i32_from_i16(i32ary, i16ary, 65536);
     memset(i16ary, 0, 65536 * sizeof(i16ary[0]));
+    checkMonotone(i32ary, 65536);
 
     memcpy_to_float_from_i32(fary, i32ary, 65536);
     memset(i32ary, 0, 65536 * sizeof(i32ary[0]));
+    checkMonotone(fary, 65536);
 
     memcpy_to_i32_from_float(i32ary, fary, 65536);
     memset(fary, 0, 65536 * sizeof(fary[0]));
+    checkMonotone(i32ary, 65536);
 
     memcpy_to_i16_from_i32(i16ary, i32ary, 65536);
     memset(i32ary, 0, 65536 * sizeof(i32ary[0]));
+    checkMonotone(i16ary, 65536);
 
-    for (size_t i = 0; i < 65536; ++i) {
-        EXPECT_EQ(((unsigned)i16ary[i] & 0xffff), (unsigned)i);
-    }
+    // do partial round-trip testing q4_27 to i16 and float
+    memcpy_to_float_from_i16(fary, i16ary, 65536);
+    //memset(i16ary, 0, 65536 * sizeof(i16ary[0])); // not cleared: we don't do full roundtrip
+
+    memcpy_to_q4_27_from_float(i32ary, fary, 65536);
+    memset(fary, 0, 65536 * sizeof(fary[0]));
+    checkMonotone(i32ary, 65536);
+
+    memcpy_to_float_from_q4_27(fary, i32ary, 65536);
+    memset(i32ary, 0, 65536 * sizeof(i32ary[0]));
+    checkMonotone(fary, 65536);
+
+    // at the end, our i16ary must be the same. (Monotone should be equivalent to this)
+    EXPECT_EQ(memcmp(i16ary, i16ref, 65536*sizeof(i16ary[0])), 0);
 
+    delete[] i16ref;
     delete[] i16ary;
     delete[] i32ary;
     delete[] fary;