--- /dev/null
+/*
+ * 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;
+}
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
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;
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;
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':
aacenc_translate_generic_text_tag,
¶ms->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;
}