apply limiter when input is float
authornu774 <honeycomb77@gmail.com>
Tue, 12 Aug 2014 14:50:49 +0000 (23:50 +0900)
committernu774 <honeycomb77@gmail.com>
Wed, 13 Aug 2014 00:26:26 +0000 (09:26 +0900)
MSVC/fdkaac.vcxproj
MSVC/fdkaac.vcxproj.filters
Makefile.am
src/extrapolater.c
src/limiter.c [new file with mode: 0644]
src/main.c
src/pcm_reader.h

index 1ef36fb6c2f3522104ef5ba7aab3f093dd807861..229e8ad93ccf8ca598832908289a7bbe90d81ec0 100644 (file)
@@ -99,6 +99,7 @@ copy ..\fdk-aac\libSYS\include\machine_type.h include\fdk-aac\ </Command>
     <ClCompile Include="..\src\caf_reader.c" />
     <ClCompile Include="..\src\compat_win32.c" />
     <ClCompile Include="..\src\extrapolater.c" />
+    <ClCompile Include="..\src\limiter.c" />
     <ClCompile Include="..\src\lpc.c" />
     <ClCompile Include="..\src\m4af.c" />
     <ClCompile Include="..\src\main.c" />
index 7d1749c3eb0a3dab5d0d43acda6b4aa004d89341..4dd9d0001eafb405e0cc1abe319dc3a54d530c5a 100644 (file)
@@ -27,6 +27,9 @@
     <ClCompile Include="..\src\extrapolater.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\limiter.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\lpc.c">
       <Filter>Source Files</Filter>
     </ClCompile>
index 1b896a7e34b6fe741cad4c79f2d897b5df4c3ee8..17b74ce3ca148d6100446226ba5a29020abeff18 100644 (file)
@@ -7,6 +7,7 @@ fdkaac_SOURCES = \
     src/aacenc.c               \
     src/caf_reader.c           \
     src/extrapolater.c         \
+    src/limiter.c              \
     src/lpc.c                  \
     src/m4af.c                 \
     src/main.c                 \
index e80d3052f9fd3494174df4396161001814bea12c..b09347817658afe58328cbf891af03416bfa1985 100644 (file)
@@ -126,7 +126,13 @@ static int process0(extrapolater_t *self, void *buffer, unsigned nframes)
         reverse_buffer(buffer, nframes, nchannels);
         reverse_buffer(bp->data, bp->count, nchannels);
     }
-    self->process = bp->count ? process1 : process2;
+    if (bp->count)
+        self->process = process1;
+    else {
+        memset(bp->data, 0, nframes * sfmt->bytes_per_frame);
+        bp->count = nframes;
+        self->process = process2;
+    }
     return nframes;
 }
 
diff --git a/src/limiter.c b/src/limiter.c
new file mode 100644 (file)
index 0000000..75eb855
--- /dev/null
@@ -0,0 +1,186 @@
+/* 
+ * Copyright (C) 2013 nu774
+ * For conditions of distribution and use, see copyright notice in COPYING
+ */
+#if HAVE_CONFIG_H
+#  include "config.h"
+#endif
+#if HAVE_STDINT_H
+#  include <stdint.h>
+#endif
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include "pcm_reader.h"
+#include "lpc.h"
+
+typedef struct buffer_t {
+    void *data;
+    unsigned count;
+    unsigned capacity;
+    unsigned head;
+} buffer_t;
+
+typedef struct limiter_t {
+    pcm_reader_vtbl_t *vtbl;
+    pcm_reader_t *src;
+    pcm_sample_description_t format;
+    int64_t position;
+    buffer_t buffers[1];
+} limiter_t;
+
+static inline pcm_reader_t *get_source(pcm_reader_t *reader)
+{
+    return ((limiter_t *)reader)->src;
+}
+
+static const
+pcm_sample_description_t *get_format(pcm_reader_t *reader)
+{
+    return pcm_get_format(get_source(reader));
+}
+
+static int64_t get_length(pcm_reader_t *reader)
+{
+    return pcm_get_length(get_source(reader));
+}
+
+static int64_t get_position(pcm_reader_t *reader)
+{
+    return ((limiter_t *)reader)->position;
+}
+
+static int reserve_buffer(buffer_t *bp, size_t required, unsigned unit)
+{
+    if (bp->capacity < required) {
+        unsigned newsize = 1;
+        void *p;
+        while (newsize < required)
+            newsize <<= 1;
+        p = realloc(bp->data, newsize * unit);
+        if (!p) return -1;
+        bp->data = p;
+        bp->capacity = newsize;
+    }
+    return 0;
+}
+
+static int read_frames(pcm_reader_t *reader, void *buffer, unsigned nframes)
+{
+    limiter_t *self = (limiter_t *)reader;
+    unsigned i, n, res, nch = self->format.channels_per_frame;
+    size_t bytes = nframes * pcm_get_format(self->src)->bytes_per_frame;
+    buffer_t *ibp = &self->buffers[nch];
+    float *obp = buffer;
+
+    do {
+        if (reserve_buffer(ibp, bytes, 1) < 0)
+           return -1;
+        res = pcm_read_frames(self->src, ibp->data, nframes);
+        for (n = 0; n < nch; ++n) {
+            float *ip = (float *)ibp->data, *x;
+            buffer_t *bp = &self->buffers[n];
+            unsigned end, limit;
+            if (reserve_buffer(bp, bp->count + res, sizeof(float)) < 0)
+                return -1;
+            x = bp->data;
+            for (i = 0; i < res; ++i)
+                x[bp->count++] = pcm_clip(ip[i * nch + n], -3.0, 3.0);
+            limit = bp->count;
+            if (limit > 0 && res > 0) {
+                float last = x[limit - 1];
+                for (; limit > 0 && x[limit-1] * last > 0; --limit)
+                    ;
+            }
+            end = bp->head;
+            while (end < limit) {
+                unsigned start, peak_pos;
+                float peak;
+                for (peak_pos = end; peak_pos < limit; ++peak_pos)
+                    if (x[peak_pos] > 1.0f || x[peak_pos] < -1.0f)
+                        break;
+                if (peak_pos == limit)
+                    break;
+                start = peak_pos;
+                peak = fabs(x[peak_pos]);
+                while (start > bp->head && x[peak_pos] * x[start] >= 0.0f)
+                    --start;
+                ++start;
+                for (end = peak_pos + 1; end < limit; ++end) {
+                    float y;
+                    if (x[peak_pos] * x[end] < 0.0f)
+                        break;
+                    y = fabs(x[end]);
+                    if (y > peak) {
+                        peak = y;
+                        peak_pos = end;
+                    }
+                }
+                if (peak < 2.0f) {
+                    float a = (peak - 1.0f) / (peak * peak);
+                    if (x[peak_pos] > 0.0f) a = -a;
+                    for (i = start; i < end; ++i)
+                        x[i] = x[i] + a * x[i] * x[i];
+                } else {
+                    float u = peak, v = 1.0f;
+                    float a = (u - 2.0f * v) / (u * u * u);
+                    float b = (3.0f * v - 2.0f * u) / (u * u);
+                    if (x[peak_pos] < 0.0f) b = -b;
+                    for (i = start; i < end; ++i)
+                        x[i] = x[i] + b * x[i] * x[i] + a * x[i] * x[i] * x[i];
+                }
+            }
+            bp->head = limit;
+        }
+        res = nframes;
+        for (n = 0; n < nch; ++n)
+            if (self->buffers[n].head < res)
+                res = self->buffers[n].head;
+        for (i = 0; i < res; ++i)
+            for (n = 0; n < nch; ++n)
+                *obp++ = ((float *)self->buffers[n].data)[i];
+        if (res) {
+            for (n = 0; n < nch; ++n) {
+                buffer_t *bp = &self->buffers[n];
+                float *p = bp->data;
+                memmove(p, p + res, (bp->count - res) * sizeof(float));
+                bp->count -= res;
+                bp->head  -= res;
+            }
+        }
+    } while (res == 0 && self->buffers[0].count);
+    self->position += res;
+    return res;
+}
+
+static void teardown(pcm_reader_t **reader)
+{
+    int i;
+    limiter_t *self = (limiter_t *)*reader;
+    pcm_teardown(&self->src);
+    for (i = 0; i < self->format.channels_per_frame + 1; ++i)
+        free(self->buffers[i].data);
+    free(self);
+    *reader = 0;
+}
+
+static pcm_reader_vtbl_t my_vtable = {
+    get_format, get_length, get_position, read_frames, teardown
+};
+
+pcm_reader_t *limiter_open(pcm_reader_t *reader)
+{
+    limiter_t *self;
+    int n = pcm_get_format(reader)->channels_per_frame;
+    size_t size = sizeof(limiter_t) + offsetof(limiter_t, buffers[n + 1]);
+
+    if ((self = calloc(1, size)) == 0)
+        return 0;
+    self->src = reader;
+    self->vtbl = &my_vtable;
+    self->format = *pcm_get_format(reader);
+    self->format.bits_per_channel = 32;
+    return (pcm_reader_t *)self;
+}
index 89af78e644bbb78566fc82cb08f8e9d9e6514897..9e104a493cc671eefdc3debc2f949c0479bfab04 100644 (file)
@@ -698,7 +698,7 @@ pcm_reader_t *open_input(aacenc_param_ex_t *params)
     if ((params->input_fp = aacenc_fopen(params->input_filename, "rb")) == 0) {
         aacenc_fprintf(stderr, "ERROR: %s: %s\n", params->input_filename,
                        strerror(errno));
-        goto END;
+        goto FAIL;
     }
     io.cookie = params->input_fp;
     if (fstat(fileno(params->input_fp), &stb) == 0
@@ -712,7 +712,7 @@ pcm_reader_t *open_input(aacenc_param_ex_t *params)
         pcm_sample_description_t desc = { 0 };
         if (parse_raw_spec(params->raw_format, &desc) < 0) {
             fprintf(stderr, "ERROR: invalid raw-format spec\n");
-            goto END;
+            goto FAIL;
         }
         desc.sample_rate = params->raw_rate;
         desc.channels_per_frame = params->raw_channels;
@@ -720,7 +720,7 @@ pcm_reader_t *open_input(aacenc_param_ex_t *params)
         desc.bytes_per_frame = params->raw_channels * bytes_per_channel;
         if ((reader = raw_open(&io, &desc)) == 0) {
             fprintf(stderr, "ERROR: failed to open raw input\n");
-            goto END;
+            goto FAIL;
         }
     } else {
         int c;
@@ -730,7 +730,7 @@ pcm_reader_t *open_input(aacenc_param_ex_t *params)
         case 'R':
             if ((reader = wav_open(&io, params->ignore_length)) == 0) {
                 fprintf(stderr, "ERROR: broken / unsupported input file\n");
-                goto END;
+                goto FAIL;
             }
             break;
         case 'c':
@@ -740,19 +740,21 @@ pcm_reader_t *open_input(aacenc_param_ex_t *params)
                                    aacenc_translate_generic_text_tag,
                                    &params->source_tag_ctx)) == 0) {
                 fprintf(stderr, "ERROR: broken / unsupported input file\n");
-                goto END;
+                goto FAIL;
             }
             break;
         default:
             fprintf(stderr, "ERROR: unsupported input file\n");
-            goto END;
+            goto FAIL;
         }
     }
-    if ((reader = pcm_open_native_converter(reader)) != 0)
-        if ((reader = pcm_open_sint16_converter(reader)) != 0)
-            reader = extrapolater_open(reader);
+    reader = pcm_open_native_converter(reader);
+    if (reader && PCM_IS_FLOAT(pcm_get_format(reader)))
+        reader = limiter_open(reader);
+    if (reader && (reader = pcm_open_sint16_converter(reader)) != 0)
+        reader = extrapolater_open(reader);
     return reader;
-END:
+FAIL:
     return 0;
 }
 
index c1150024f751390bda49cfb09fe246b7d5cead1f..68c3c555171912db5c05100025b510b130dd954f 100644 (file)
@@ -119,5 +119,6 @@ pcm_reader_t *pcm_open_float_converter(pcm_reader_t *reader);
 pcm_reader_t *pcm_open_sint16_converter(pcm_reader_t *reader);
 
 pcm_reader_t *extrapolater_open(pcm_reader_t *reader);
+pcm_reader_t *limiter_open(pcm_reader_t *reader);
 
 #endif
This page took 0.018096 seconds and 4 git commands to generate.