* 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].
*/
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:
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.
*
static const float limneg = -1.;
if (f <= limneg) {
- return -0x80000000;
+ return -0x80000000; /* or 0x80000000 */
} else if (f >= limpos) {
return 0x7fffffff;
}
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) {
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) {
}
}
-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,
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?
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;