Add support for Q8.23 data in audio primitives

Change-Id: I420f1564c0f2c46c81e4596b9d76506e1ebc4a14
Signed-off-by: Andy Hung <hunga@google.com>
diff --git a/audio_utils/include/audio_utils/primitives.h b/audio_utils/include/audio_utils/primitives.h
index cb69a41..01f930d 100644
--- a/audio_utils/include/audio_utils/primitives.h
+++ b/audio_utils/include/audio_utils/primitives.h
@@ -161,6 +161,54 @@
  */
 void memcpy_to_p24_from_float(uint8_t *dst, const float *src, size_t count);
 
+/* Copy samples from signed fixed point 16-bit Q0.15 to signed fixed-point 32-bit Q8.23.
+ * The output data range is [0xff800000, 0x007fff00] at intervals of 0x100.
+ * Parameters:
+ *  dst     Destination buffer
+ *  src     Source buffer
+ *  count   Number of samples to copy
+ * The destination and source buffers must be completely separate.
+ */
+void memcpy_to_q8_23_from_i16(int32_t *dst, const int16_t *src, size_t count);
+
+/* Copy samples from single-precision floating-point to signed fixed-point 32-bit Q8.23.
+ * This copy will clamp the Q8.23 representation to [0xff800000, 0x007fffff] even though there
+ * are guard bits available. Fractional lsb is rounded to nearest, ties away from zero.
+ * See clamp24_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_q8_23_from_float_with_clamp(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:
+ *  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_i16_from_q8_23(int16_t *dst, const int32_t *src, size_t count);
+
+/* Copy samples from signed fixed-point 32-bit Q8.23 to single-precision floating-point.
+ * The nominal output float range is [-1.0, 1.0) for the fixed-point
+ * range [0xff800000, 0x007fffff]. The maximum output float range is [-256.0, 256.0).
+ * No rounding is needed as the representation is exact for nominal values.
+ * Rounding for overflow values is to nearest, ties to even.
+ * 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_float_from_q8_23(float *dst, const int32_t *src, size_t count);
+
 /* Copy samples from signed fixed point 16-bit Q0.15 to signed fixed-point 32-bit Q0.31.
  * The output data range is [0x80000000, 0x7fff0000] at intervals of 0x10000.
  * Parameters:
@@ -424,6 +472,20 @@
     return float_from_i32(i32_from_p24(packed24));
 }
 
+/* Convert a 24-bit Q8.23 value to single-precision floating-point.
+ * The nominal output float range is [-1.0, 1.0) for the fixed-point
+ * range [0xff800000, 0x007fffff].  The maximum float range is [-256.0, 256.0).
+ *
+ * There is no rounding in the nominal range, the conversion and representation
+ * is exact. For values outside the nominal range, rounding is to nearest, ties to even.
+ */
+static inline float float_from_q8_23(int32_t ival)
+{
+    static const float scale = 1. / (float)(1UL << 23);
+
+    return ival * scale;
+}
+
 /**
  * Multiply-accumulate 16-bit terms with 32-bit result: return a + in*v.
  */
diff --git a/audio_utils/primitives.c b/audio_utils/primitives.c
index 9a50bac..91e1bbb 100644
--- a/audio_utils/primitives.c
+++ b/audio_utils/primitives.c
@@ -127,6 +127,34 @@
     }
 }
 
+void memcpy_to_q8_23_from_i16(int32_t *dst, const int16_t *src, size_t count)
+{
+    while (count--) {
+        *dst++ = (int32_t)*src++ << 8;
+    }
+}
+
+void memcpy_to_q8_23_from_float_with_clamp(int32_t *dst, const float *src, size_t count)
+{
+    while (count--) {
+        *dst++ = clamp24_from_float(*src++);
+    }
+}
+
+void memcpy_to_i16_from_q8_23(int16_t *dst, const int32_t *src, size_t count)
+{
+    while (count--) {
+        *dst++ = clamp16(*src++ >> 8);
+    }
+}
+
+void memcpy_to_float_from_q8_23(float *dst, const int32_t *src, size_t count)
+{
+    while (count--) {
+        *dst++ = float_from_q8_23(*src++);
+    }
+}
+
 void memcpy_to_i32_from_i16(int32_t *dst, const int16_t *src, size_t count)
 {
     while (count--) {