shithub: mp3dec

Download patch

ref: f912b8420f1ee8b5617bd155c6faa8f870375769
parent: 19b619acde594e882412c25420169fabdc256120
author: Chris Robinson <chris.kcat@gmail.com>
date: Wed Aug 22 00:12:38 EDT 2018

Add float output support with the MINIMP3_FLOAT_OUTPUT macro

When defined, mp3dec_decode_frame changes to output float samples (without
clamping or 16-bit quantization), and the new method mp3dec_f32_to_s16 is
defined to help convert float samples to signed 16-bit.

--- a/minimp3.h
+++ b/minimp3.h
@@ -26,12 +26,16 @@
 #endif
 
 void mp3dec_init(mp3dec_t *dec);
+#ifndef MINIMP3_FLOAT_OUTPUT
 int mp3dec_decode_frame(mp3dec_t *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, mp3dec_frame_info_t *info);
+#else
+int mp3dec_decode_frame(mp3dec_t *dec, const unsigned char *mp3, int mp3_bytes, float *pcm, mp3dec_frame_info_t *info);
+void mp3dec_f32_to_s16(const float *in, short *out, int num_samples);
+#endif
 
 #ifdef __cplusplus
 }
 #endif
-#endif /*MINIMP3_H*/
 
 #ifdef MINIMP3_IMPLEMENTATION
 
@@ -1365,19 +1369,28 @@
 #endif
 }
 
+#ifndef MINIMP3_FLOAT_OUTPUT
+typedef short mp3d_sample_t;
+
 static short mp3d_scale_pcm(float sample)
 {
-    if (sample >  32767.0) return (short) 32767;
-    if (sample < -32768.0) return (short)-32768;
-    int s = (int)(sample + .5f);
+    if (sample >=  32766.5) return (short) 32767;
+    if (sample <= -32767.5) return (short)-32768;
+    short s = (short)(sample + .5f);
     s -= (s < 0);   /* away from zero, to be compliant */
-    if (s >  32767) return (short) 32767;
-    if (s < -32768) return (short)-32768;
-    return (short)s;
+    return s;
 }
+#else
+typedef float mp3d_sample_t;
 
-static void mp3d_synth_pair(short *pcm, int nch, const float *z)
+static float mp3d_scale_pcm(float sample)
 {
+    return sample / 32768.0f;
+}
+#endif
+
+static void mp3d_synth_pair(mp3d_sample_t *pcm, int nch, const float *z)
+{
     float a;
     a  = (z[14*64] - z[    0]) * 29;
     a += (z[ 1*64] + z[13*64]) * 213;
@@ -1401,11 +1414,11 @@
     pcm[16*nch] = mp3d_scale_pcm(a);
 }
 
-static void mp3d_synth(float *xl, short *dstl, int nch, float *lins)
+static void mp3d_synth(float *xl, mp3d_sample_t *dstl, int nch, float *lins)
 {
     int i;
     float *xr = xl + 576*(nch - 1);
-    short *dstr = dstl + (nch - 1);
+    mp3d_sample_t *dstr = dstl + (nch - 1);
 
     static const float g_win[] = {
         -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
@@ -1462,6 +1475,7 @@
         V0(0) V2(1) V1(2) V2(3) V1(4) V2(5) V1(6) V2(7)
 
         {
+#ifndef MINIMP3_FLOAT_OUTPUT
 #if HAVE_SSE
             static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
             static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
@@ -1490,6 +1504,32 @@
             vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
             vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
 #endif
+
+#else
+
+            static const f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
+            a = VMUL(a, g_scale);
+            b = VMUL(b, g_scale);
+#if HAVE_SSE
+            _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
+            _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
+            _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
+            _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
+            _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
+            _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
+            _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
+            _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
+#else
+            vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
+            vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
+            vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
+            vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
+            vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
+            vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
+            vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
+            vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
+#endif
+#endif /* MINIMP3_FLOAT_OUTPUT */
         }
     } else
 #endif
@@ -1527,7 +1567,7 @@
 #endif
 }
 
-static void mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, short *pcm, float *lins)
+static void mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, mp3d_sample_t *pcm, float *lins)
 {
     int i;
     for (i = 0; i < nch; i++)
@@ -1611,7 +1651,7 @@
     dec->header[0] = 0;
 }
 
-int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, short *pcm, mp3dec_frame_info_t *info)
+int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_sample_t *pcm, mp3dec_frame_info_t *info)
 {
     int i = 0, igr, frame_size = 0, success = 1;
     const uint8_t *hdr;
@@ -1704,4 +1744,68 @@
     }
     return success*hdr_frame_samples(dec->header);
 }
+
+#ifdef MINIMP3_FLOAT_OUTPUT
+void mp3dec_f32_to_s16(const float *in, short *out, int num_samples)
+{
+    if(num_samples > 0)
+    {
+        int i = 0;
+#if HAVE_SIMD
+        int aligned_count = num_samples & ~7;
+
+        for(;i < aligned_count;i+=8)
+        {
+            static const f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f };
+            f4 a = VMUL(VLD(&in[i  ]), g_scale);
+            f4 b = VMUL(VLD(&in[i+4]), g_scale);
+#if HAVE_SSE
+            static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
+            static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
+            __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
+                                           _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
+            out[i  ] = _mm_extract_epi16(pcm8, 0);
+            out[i+1] = _mm_extract_epi16(pcm8, 1);
+            out[i+2] = _mm_extract_epi16(pcm8, 2);
+            out[i+3] = _mm_extract_epi16(pcm8, 3);
+            out[i+4] = _mm_extract_epi16(pcm8, 4);
+            out[i+5] = _mm_extract_epi16(pcm8, 5);
+            out[i+6] = _mm_extract_epi16(pcm8, 6);
+            out[i+7] = _mm_extract_epi16(pcm8, 7);
+#else
+            int16x4_t pcma, pcmb;
+            a = VADD(a, VSET(0.5f));
+            b = VADD(b, VSET(0.5f));
+            pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0)))));
+            pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0)))));
+            vst1_lane_s16(out+i  , pcma, 0);
+            vst1_lane_s16(out+i+1, pcma, 1);
+            vst1_lane_s16(out+i+2, pcma, 2);
+            vst1_lane_s16(out+i+3, pcma, 3);
+            vst1_lane_s16(out+i+4, pcmb, 0);
+            vst1_lane_s16(out+i+5, pcmb, 1);
+            vst1_lane_s16(out+i+6, pcmb, 2);
+            vst1_lane_s16(out+i+7, pcmb, 3);
+#endif
+        }
+#endif
+        for(;i < num_samples;i++)
+        {
+            float sample = in[i] * 32768.0f;
+            if(sample >=  32766.5)
+                out[i] = (short) 32767;
+            else if (sample <= -32767.5)
+                out[i] = (short)-32768;
+            else
+            {
+                short s = (short)(sample + .5f);
+                s -= (s < 0);   /* away from zero, to be compliant */
+                out[i] = s;
+            }
+        }
+    }
+}
+#endif
+
 #endif /*MINIMP3_IMPLEMENTATION*/
+#endif /*MINIMP3_H*/