OSDN Git Service

Add floating point to unsigned 8 bit conversion
authorAndy Hung <hunga@google.com>
Tue, 13 Jan 2015 21:56:37 +0000 (13:56 -0800)
committerAndy Hung <hunga@google.com>
Tue, 13 Jan 2015 23:52:37 +0000 (15:52 -0800)
Change-Id: Icc4591ab8f594f9c203644812208b227442d1f05

audio_utils/format.c
audio_utils/include/audio_utils/format.h
audio_utils/include/audio_utils/primitives.h
audio_utils/primitives.c

index e91e057..4ac862a 100644 (file)
@@ -29,6 +29,7 @@ void memcpy_by_audio_format(void *dst, audio_format_t dst_format,
         switch (dst_format) {
         case AUDIO_FORMAT_PCM_16_BIT:
         case AUDIO_FORMAT_PCM_FLOAT:
+        case AUDIO_FORMAT_PCM_8_BIT:
         case AUDIO_FORMAT_PCM_24_BIT_PACKED:
         case AUDIO_FORMAT_PCM_32_BIT:
         case AUDIO_FORMAT_PCM_8_24_BIT:
@@ -44,6 +45,9 @@ void memcpy_by_audio_format(void *dst, audio_format_t dst_format,
         case AUDIO_FORMAT_PCM_FLOAT:
             memcpy_to_i16_from_float((int16_t*)dst, (float*)src, count);
             return;
+        case AUDIO_FORMAT_PCM_8_BIT:
+            memcpy_to_i16_from_u8((int16_t*)dst, (uint8_t*)src, count);
+            return;
         case AUDIO_FORMAT_PCM_24_BIT_PACKED:
             memcpy_to_i16_from_p24((int16_t*)dst, (uint8_t*)src, count);
             return;
@@ -62,6 +66,9 @@ void memcpy_by_audio_format(void *dst, audio_format_t dst_format,
         case AUDIO_FORMAT_PCM_16_BIT:
             memcpy_to_float_from_i16((float*)dst, (int16_t*)src, count);
             return;
+        case AUDIO_FORMAT_PCM_8_BIT:
+            memcpy_to_float_from_u8((float*)dst, (uint8_t*)src, count);
+            return;
         case AUDIO_FORMAT_PCM_24_BIT_PACKED:
             memcpy_to_float_from_p24((float*)dst, (uint8_t*)src, count);
             return;
@@ -75,6 +82,18 @@ void memcpy_by_audio_format(void *dst, audio_format_t dst_format,
             break;
         }
         break;
+    case AUDIO_FORMAT_PCM_8_BIT:
+        switch (src_format) {
+        case AUDIO_FORMAT_PCM_16_BIT:
+            memcpy_to_u8_from_i16((uint8_t*)dst, (int16_t*)src, count);
+            return;
+        case AUDIO_FORMAT_PCM_FLOAT:
+            memcpy_to_u8_from_float((uint8_t*)dst, (float*)src, count);
+            return;
+        default:
+            break;
+        }
+        break;
     case AUDIO_FORMAT_PCM_24_BIT_PACKED:
         switch (src_format) {
         case AUDIO_FORMAT_PCM_16_BIT:
index 07e3935..b9b173e 100644 (file)
@@ -38,6 +38,7 @@ __BEGIN_DECLS
  *
  * AUDIO_FORMAT_PCM_16_BIT
  * AUDIO_FORMAT_PCM_FLOAT
+ * AUDIO_FORMAT_PCM_8_BIT
  * AUDIO_FORMAT_PCM_24_BIT_PACKED
  * AUDIO_FORMAT_PCM_32_BIT
  * AUDIO_FORMAT_PCM_8_24_BIT
index b93f649..ac0c0a9 100644 (file)
@@ -68,6 +68,17 @@ void memcpy_to_i16_from_u8(int16_t *dst, const uint8_t *src, size_t count);
  */
 void memcpy_to_u8_from_i16(uint8_t *dst, const int16_t *src, size_t count);
 
+/* Copy samples from float to unsigned 8-bit offset by 0x80.
+ * 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.
+ * The conversion is done by truncation, without dithering, so it loses resolution.
+ */
+void memcpy_to_u8_from_float(uint8_t *dst, const float *src, size_t count);
+
 /* Shrink and copy samples from signed 32-bit fixed-point Q0.31 to signed 16-bit Q0.15.
  * Parameters:
  *  dst     Destination buffer
@@ -116,6 +127,17 @@ void memcpy_to_float_from_q4_27(float *dst, const int32_t *src, size_t count);
  */
 void memcpy_to_float_from_i16(float *dst, const int16_t *src, size_t count);
 
+/* Copy samples from unsigned fixed-point 8 bit to single-precision floating-point.
+ * The output float range is [-1.0, 1.0) for the fixed-point range [0x00, 0xFF].
+ * No rounding is needed as the representation is exact.
+ * 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_float_from_u8(float *dst, const uint8_t *src, size_t count);
+
 /* Copy samples from signed fixed-point packed 24 bit Q0.23 to single-precision floating-point.
  * The packed 24 bit input is stored in native endian format in a uint8_t byte array.
  * The output float range is [-1.0, 1.0) for the fixed-point range [0x800000, 0x7fffff].
@@ -426,6 +448,45 @@ static inline int16_t clamp16_from_float(float f)
     return u.i; /* Return lower 16 bits, the part of interest in the significand. */
 }
 
+/*
+ * Convert a IEEE 754 single precision float [-1.0, 1.0) to uint8_t [0, 0xff]
+ * with clamping.  Note the open bound at 1.0, values within 1/128 of 1.0 map
+ * to 255 instead of 256 (early clamping due to the smaller positive integer subrange).
+ *
+ * Values outside the range [-1.0, 1.0) are properly clamped to 0 and 255,
+ * including -Inf and +Inf. NaN will generally be treated either as 0 or 255,
+ * depending on the sign bit inside NaN (whose representation is not unique).
+ * Nevertheless, strictly speaking, NaN behavior should be considered undefined.
+ *
+ * Rounding of 0.5 lsb is to even (default for IEEE 754).
+ */
+static inline uint8_t clamp8_from_float(float f)
+{
+    /* Offset is used to expand the valid range of [-1.0, 1.0) into the 16 lsbs of the
+     * floating point significand. The normal shift is 3<<22, but the -7 offset
+     * is used to multiply by 128.
+     */
+    static const float offset = (float)((3 << (22 - 7)) + 1 /* to cancel -1.0 */);
+    /* zero = (0x11f << 22) =  0x47c00000 */
+    static const int32_t limneg = (0x11f << 22) /*zero*/;
+    static const int32_t limpos = (0x11f << 22) /*zero*/ + 255; /* 0x47c000ff */
+
+    union {
+        float f;
+        int32_t i;
+    } u;
+
+    u.f = f + offset; /* recenter valid range */
+    /* Now the valid range is represented as integers between [limneg, limpos].
+     * Clamp using the fact that float representation (as an integer) is an ordered set.
+     */
+    if (u.i < limneg)
+        return 0;
+    if (u.i > limpos)
+        return 255;
+    return u.i; /* Return lower 8 bits, the part of interest in the significand. */
+}
+
 /* Convert a single-precision floating point value to a Q0.23 integer value, stored in a
  * 32 bit signed integer (technically stored as Q8.23, but clamped to Q0.23).
  *
@@ -636,6 +697,17 @@ static inline float float_from_i16(int16_t ival)
     return ival * scale;
 }
 
+/* Convert an unsigned fixed-point 8-bit U0.8 value to single-precision floating-point.
+ * The nominal output float range is [-1.0, 1.0) if the fixed-point range is
+ * [0x00, 0xff].
+ */
+static inline float float_from_u8(uint8_t uval)
+{
+    static const float scale = 1. / (float)(1UL << 7);
+
+    return ((int)uval - 128) * scale;
+}
+
 /* Convert a packed 24bit Q0.23 value stored native-endian in a uint8_t ptr
  * to a signed fixed-point 32 bit integer Q0.31 value. The output Q0.31 range
  * is [0x80000000, 0x7fffff00] for the fixed-point range [0x800000, 0x7fffff].
index 1cc4588..ae9b28c 100644 (file)
@@ -48,6 +48,13 @@ void memcpy_to_u8_from_i16(uint8_t *dst, const int16_t *src, size_t count)
     }
 }
 
+void memcpy_to_u8_from_float(uint8_t *dst, const float *src, size_t count)
+{
+    while (count--) {
+        *dst++ = clamp8_from_float(*src++);
+    }
+}
+
 void memcpy_to_i16_from_i32(int16_t *dst, const int32_t *src, size_t count)
 {
     while (count--) {
@@ -76,6 +83,13 @@ void memcpy_to_float_from_i16(float *dst, const int16_t *src, size_t count)
     }
 }
 
+void memcpy_to_float_from_u8(float *dst, const uint8_t *src, size_t count)
+{
+    while (count--) {
+        *dst++ = float_from_u8(*src++);
+    }
+}
+
 void memcpy_to_float_from_p24(float *dst, const uint8_t *src, size_t count)
 {
     while (count--) {