summaryrefslogtreecommitdiff
path: root/libs/audiographer/src
diff options
context:
space:
mode:
Diffstat (limited to 'libs/audiographer/src')
-rw-r--r--libs/audiographer/src/gdither/gdither.cc474
-rw-r--r--libs/audiographer/src/gdither/gdither.h92
-rw-r--r--libs/audiographer/src/gdither/gdither_types.h48
-rw-r--r--libs/audiographer/src/gdither/gdither_types_internal.h74
-rw-r--r--libs/audiographer/src/gdither/noise.h38
-rw-r--r--libs/audiographer/src/routines.cc7
-rw-r--r--libs/audiographer/src/sample_format_converter.cc190
-rw-r--r--libs/audiographer/src/sndfile_base.cc56
-rw-r--r--libs/audiographer/src/sndfile_reader.cc67
-rw-r--r--libs/audiographer/src/sndfile_writer.cc73
-rw-r--r--libs/audiographer/src/sr_converter.cc218
-rw-r--r--libs/audiographer/src/utils.cc14
12 files changed, 1351 insertions, 0 deletions
diff --git a/libs/audiographer/src/gdither/gdither.cc b/libs/audiographer/src/gdither/gdither.cc
new file mode 100644
index 0000000000..966da47b06
--- /dev/null
+++ b/libs/audiographer/src/gdither/gdither.cc
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "gdither_types_internal.h"
+#include "gdither.h"
+#include "noise.h"
+
+/* this monstrosity is necessary to get access to lrintf() and random().
+ whoever is writing the glibc headers <cmath> and <cstdlib> should be
+ hauled off to a programmer re-education camp. for the rest of
+ their natural lives. or longer. <paul@linuxaudiosystems.com>
+*/
+
+#define _ISOC9X_SOURCE 1
+#define _ISOC99_SOURCE 1
+#ifdef __cplusplus
+#include <cmath>
+#else
+#include <math.h>
+#endif
+
+#undef __USE_SVID
+#define __USE_SVID 1
+#ifdef __cplusplus
+#include <cstdlib>
+#else
+#include <stdlib.h>
+#endif
+
+#include <sys/types.h>
+
+/* Lipshitz's minimally audible FIR, only really works for 46kHz-ish signals */
+static const float shaped_bs[] = { 2.033f, -2.165f, 1.959f, -1.590f, 0.6149f };
+
+/* Some useful constants */
+#define MAX_U8 255
+#define MIN_U8 0
+#define SCALE_U8 128.0f
+
+#define MAX_S16 32767
+#define MIN_S16 -32768
+#define SCALE_S16 32768.0f
+
+#define MAX_S24 8388607
+#define MIN_S24 -8388608
+#define SCALE_S24 8388608.0f
+
+GDither gdither_new(GDitherType type, uint32_t channels,
+
+ GDitherSize bit_depth, int dither_depth)
+{
+ GDither s;
+
+ s = (GDither)calloc(1, sizeof(struct GDither_s));
+ s->type = type;
+ s->channels = channels;
+ s->bit_depth = (int)bit_depth;
+
+ if (dither_depth <= 0 || dither_depth > (int)bit_depth) {
+ dither_depth = (int)bit_depth;
+ }
+ s->dither_depth = dither_depth;
+
+ s->scale = (float)(1LL << (dither_depth - 1));
+ if (bit_depth == GDitherFloat || bit_depth == GDitherDouble) {
+ s->post_scale_fp = 1.0f / s->scale;
+ s->post_scale = 0;
+ } else {
+ s->post_scale_fp = 0.0f;
+ s->post_scale = 1 << ((int)bit_depth - dither_depth);
+ }
+
+ switch (bit_depth) {
+ case GDither8bit:
+ /* Unsigned 8 bit */
+ s->bias = 1.0f;
+ s->clamp_u = 255;
+ s->clamp_l = 0;
+ break;
+ case GDither16bit:
+ /* Signed 16 bit */
+ s->bias = 0.0f;
+ s->clamp_u = 32767;
+ s->clamp_l = -32768;
+ break;
+ case GDither32bit:
+ /* Signed 24 bit, in upper 24 bits of 32 bit word */
+ s->bias = 0.0f;
+ s->clamp_u = 8388607;
+ s->clamp_l = -8388608;
+ break;
+ case GDitherFloat:
+ /* normalised float */
+ s->bias = 0.0f;
+ s->clamp_u = lrintf(s->scale);
+ s->clamp_l = lrintf(-s->scale);
+ break;
+ case GDitherDouble:
+ /* normalised float */
+ s->bias = 0.0f;
+ s->clamp_u = lrintf(s->scale);
+ s->clamp_l = lrintf(-s->scale);
+ break;
+ case 23:
+ /* special performance test case */
+ s->scale = SCALE_S24;
+ s->post_scale = 256;
+ s->bias = 0.0f;
+ s->clamp_u = 8388607;
+ s->clamp_l = -8388608;
+ break;
+ default:
+ /* Not a bit depth we can handle */
+ free(s);
+
+ return NULL;
+ break;
+ }
+
+ switch (type) {
+ case GDitherNone:
+ case GDitherRect:
+ /* No state */
+ break;
+
+ case GDitherTri:
+ /* The last whitenoise sample */
+ s->tri_state = (float *) calloc(channels, sizeof(float));
+ break;
+
+ case GDitherShaped:
+ /* The error from the last few samples encoded */
+ s->shaped_state = (GDitherShapedState*)
+ calloc(channels, sizeof(GDitherShapedState));
+ break;
+ }
+
+ return s;
+}
+
+void gdither_free(GDither s)
+{
+ if (s) {
+ free(s->tri_state);
+ free(s->shaped_state);
+ free(s);
+ }
+}
+
+inline static void gdither_innner_loop(const GDitherType dt,
+ const uint32_t stride, const float bias, const float scale,
+
+ const uint32_t post_scale, const int bit_depth,
+ const uint32_t channel, const uint32_t length, float *ts,
+
+ GDitherShapedState *ss, float const *x, void *y, const int clamp_u,
+
+ const int clamp_l)
+{
+ uint32_t pos, i;
+ uint8_t *o8 = (uint8_t*) y;
+ int16_t *o16 = (int16_t*) y;
+ int32_t *o32 = (int32_t*) y;
+ float tmp, r, ideal;
+ int64_t clamped;
+
+ i = channel;
+ for (pos = 0; pos < length; pos++, i += stride) {
+ tmp = x[i] * scale + bias;
+
+ switch (dt) {
+ case GDitherNone:
+ break;
+ case GDitherRect:
+ tmp -= GDITHER_NOISE;
+ break;
+ case GDitherTri:
+ r = GDITHER_NOISE - 0.5f;
+ tmp -= r - ts[channel];
+ ts[channel] = r;
+ break;
+ case GDitherShaped:
+ /* Save raw value for error calculations */
+ ideal = tmp;
+
+ /* Run FIR and add white noise */
+ ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f;
+ tmp += ss->buffer[ss->phase] * shaped_bs[0]
+ + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[1]
+ + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[2]
+ + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[3]
+ + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[4];
+
+ /* Roll buffer and store last error */
+ ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK;
+ ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal;
+ break;
+ }
+
+ clamped = lrintf(tmp);
+ if (clamped > clamp_u) {
+ clamped = clamp_u;
+ } else if (clamped < clamp_l) {
+ clamped = clamp_l;
+ }
+
+ switch (bit_depth) {
+ case GDither8bit:
+ o8[i] = (u_int8_t) (clamped * post_scale);
+ break;
+ case GDither16bit:
+ o16[i] = (int16_t) (clamped * post_scale);
+ break;
+ case GDither32bit:
+ o32[i] = (int32_t) (clamped * post_scale);
+ break;
+ }
+ }
+}
+
+/* floating pint version of the inner loop function */
+inline static void gdither_innner_loop_fp(const GDitherType dt,
+ const uint32_t stride, const float bias, const float scale,
+
+ const float post_scale, const int bit_depth,
+ const uint32_t channel, const uint32_t length, float *ts,
+
+ GDitherShapedState *ss, float const *x, void *y, const int clamp_u,
+
+ const int clamp_l)
+{
+ uint32_t pos, i;
+ float *oflt = (float*) y;
+ double *odbl = (double*) y;
+ float tmp, r, ideal;
+ double clamped;
+
+ i = channel;
+ for (pos = 0; pos < length; pos++, i += stride) {
+ tmp = x[i] * scale + bias;
+
+ switch (dt) {
+ case GDitherNone:
+ break;
+ case GDitherRect:
+ tmp -= GDITHER_NOISE;
+ break;
+ case GDitherTri:
+ r = GDITHER_NOISE - 0.5f;
+ tmp -= r - ts[channel];
+ ts[channel] = r;
+ break;
+ case GDitherShaped:
+ /* Save raw value for error calculations */
+ ideal = tmp;
+
+ /* Run FIR and add white noise */
+ ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f;
+ tmp += ss->buffer[ss->phase] * shaped_bs[0]
+ + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[1]
+ + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[2]
+ + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[3]
+ + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[4];
+
+ /* Roll buffer and store last error */
+ ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK;
+ ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal;
+ break;
+ }
+
+ clamped = rintf(tmp);
+ if (clamped > clamp_u) {
+ clamped = clamp_u;
+ } else if (clamped < clamp_l) {
+ clamped = clamp_l;
+ }
+
+ switch (bit_depth) {
+ case GDitherFloat:
+ oflt[i] = (float) (clamped * post_scale);
+ break;
+ case GDitherDouble:
+ odbl[i] = (double) (clamped * post_scale);
+ break;
+ }
+ }
+}
+
+#define GDITHER_CONV_BLOCK 512
+
+void gdither_run(GDither s, uint32_t channel, uint32_t length,
+ double const *x, void *y)
+{
+ float conv[GDITHER_CONV_BLOCK];
+ uint32_t i, pos;
+ char *ycast = (char *)y;
+
+ int step;
+
+ switch (s->bit_depth) {
+ case GDither8bit:
+ step = 1;
+ break;
+ case GDither16bit:
+ step = 2;
+ break;
+ case GDither32bit:
+ case GDitherFloat:
+ step = 4;
+ break;
+ case GDitherDouble:
+ step = 8;
+ break;
+ default:
+ step = 0;
+ break;
+ }
+
+ pos = 0;
+ while (pos < length) {
+ for (i=0; (i + pos) < length && i < GDITHER_CONV_BLOCK; i++) {
+ conv[i] = x[pos + i];
+ }
+ gdither_runf(s, channel, i, conv, ycast + s->channels * step);
+ pos += i;
+ }
+}
+
+void gdither_runf(GDither s, uint32_t channel, uint32_t length,
+ float const *x, void *y)
+{
+ uint32_t pos, i;
+ float tmp;
+ int64_t clamped;
+ GDitherShapedState *ss = NULL;
+
+ if (!s || channel >= s->channels) {
+ return;
+ }
+
+ if (s->shaped_state) {
+ ss = s->shaped_state + channel;
+ }
+
+ if (s->type == GDitherNone && s->bit_depth == 23) {
+ int32_t *o32 = (int32_t*) y;
+
+ for (pos = 0; pos < length; pos++) {
+ i = channel + (pos * s->channels);
+ tmp = x[i] * 8388608.0f;
+
+ clamped = lrintf(tmp);
+ if (clamped > 8388607) {
+ clamped = 8388607;
+ } else if (clamped < -8388608) {
+ clamped = -8388608;
+ }
+
+ o32[i] = (int32_t) (clamped * 256);
+ }
+
+ return;
+ }
+
+ /* some common case handling code - looks a bit wierd, but it allows
+ * the compiler to optimise out the branches in the inner loop */
+ if (s->bit_depth == 8 && s->dither_depth == 8) {
+ switch (s->type) {
+ case GDitherNone:
+ gdither_innner_loop(GDitherNone, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, NULL, NULL, x, y,
+ MAX_U8, MIN_U8);
+ break;
+ case GDitherRect:
+ gdither_innner_loop(GDitherRect, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, NULL, NULL, x, y,
+ MAX_U8, MIN_U8);
+ break;
+ case GDitherTri:
+ gdither_innner_loop(GDitherTri, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, s->tri_state,
+ NULL, x, y, MAX_U8, MIN_U8);
+ break;
+ case GDitherShaped:
+ gdither_innner_loop(GDitherShaped, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, NULL,
+ ss, x, y, MAX_U8, MIN_U8);
+ break;
+ }
+ } else if (s->bit_depth == 16 && s->dither_depth == 16) {
+ switch (s->type) {
+ case GDitherNone:
+ gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S16,
+ 1, 16, channel, length, NULL, NULL, x, y,
+ MAX_S16, MIN_S16);
+ break;
+ case GDitherRect:
+ gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S16,
+ 1, 16, channel, length, NULL, NULL, x, y,
+ MAX_S16, MIN_S16);
+ break;
+ case GDitherTri:
+ gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S16,
+ 1, 16, channel, length, s->tri_state,
+ NULL, x, y, MAX_S16, MIN_S16);
+ break;
+ case GDitherShaped:
+ gdither_innner_loop(GDitherShaped, s->channels, 0.0f,
+ SCALE_S16, 1, 16, channel, length, NULL,
+ ss, x, y, MAX_S16, MIN_S16);
+ break;
+ }
+ } else if (s->bit_depth == 32 && s->dither_depth == 24) {
+ switch (s->type) {
+ case GDitherNone:
+ gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length, NULL, NULL, x,
+ y, MAX_S24, MIN_S24);
+ break;
+ case GDitherRect:
+ gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length, NULL, NULL, x,
+ y, MAX_S24, MIN_S24);
+ break;
+ case GDitherTri:
+ gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length, s->tri_state,
+ NULL, x, y, MAX_S24, MIN_S24);
+ break;
+ case GDitherShaped:
+ gdither_innner_loop(GDitherShaped, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length,
+ NULL, ss, x, y, MAX_S24, MIN_S24);
+ break;
+ }
+ } else if (s->bit_depth == GDitherFloat || s->bit_depth == GDitherDouble) {
+ gdither_innner_loop_fp(s->type, s->channels, s->bias, s->scale,
+ s->post_scale_fp, s->bit_depth, channel, length,
+ s->tri_state, ss, x, y, s->clamp_u, s->clamp_l);
+ } else {
+ /* no special case handling, just process it from the struct */
+
+ gdither_innner_loop(s->type, s->channels, s->bias, s->scale,
+ s->post_scale, s->bit_depth, channel,
+ length, s->tri_state, ss, x, y, s->clamp_u,
+ s->clamp_l);
+ }
+}
+
+/* vi:set ts=8 sts=4 sw=4: */
diff --git a/libs/audiographer/src/gdither/gdither.h b/libs/audiographer/src/gdither/gdither.h
new file mode 100644
index 0000000000..d2b2657f32
--- /dev/null
+++ b/libs/audiographer/src/gdither/gdither.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef GDITHER_H
+#define GDITHER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gdither_types.h"
+
+/* Create and initialise a state structure, takes a dither type, a number of
+ * channels and a bit depth as input
+ *
+ * The Dither type is one of
+ *
+ * GDitherNone - straight nearest neighbour rounding. Theres no pressing
+ * reason to do this at 8 or 16 bit, but you might want to at 24, for some
+ * reason. At the lest it will save you writing int->float conversion code,
+ * which is arder than it sounds.
+ *
+ * GDitherRect - mathematically most accurate, lowest noise floor, but not
+ * that good for audio. It is the fastest though.
+ *
+ * GDitherTri - a happy medium between Rectangular and Shaped, reasonable
+ * noise floor, not too obvious, quite fast.
+ *
+ * GDitherShaped - should have the least audible impact, but has the highest
+ * noise floor, fairly CPU intensive. Not advisible if your going to apply
+ * any frequency manipulation afterwards.
+ *
+ * channels, sets the number of channels in the output data, output data will
+ * be written interleaved into the area given to gdither_run(). Set to 1
+ * if you are not working with interleaved buffers.
+ *
+ * bit depth, sets the bit width of the output sample data, it can be one of:
+ *
+ * GDither8bit - 8 bit unsiged
+ * GDither16bit - 16 bit signed
+ * GDither32bit - 24+bits in upper bits of a 32 bit word
+ * GDitherFloat - IEEE floating point (32bits)
+ * GDitherDouble - Double precision IEEE floating point (64bits)
+ *
+ * dither_depth, set the number of bits before the signal will be truncated to,
+ * eg. 16 will produce an output stream with 16bits-worth of signal. Setting to
+ * zero or greater than the width of the output format will dither to the
+ * maximum precision allowed by the output format.
+ */
+GDither gdither_new(GDitherType type, uint32_t channels,
+
+ GDitherSize bit_depth, int dither_depth);
+
+/* Frees memory used by gdither_new.
+ */
+void gdither_free(GDither s);
+
+/* Applies dithering to the supplied signal.
+ *
+ * channel is the channel number you are processing (0 - channles-1), length is
+ * the length of the input, in samples, x is the input samples (float), y is
+ * where the output samples will be written, it should have the approaprate
+ * type for the chosen bit depth
+ */
+void gdither_runf(GDither s, uint32_t channel, uint32_t length,
+ float const *x, void *y);
+
+/* see gdither_runf, vut input argument is double format */
+void gdither_run(GDither s, uint32_t channel, uint32_t length,
+ double const *x, void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libs/audiographer/src/gdither/gdither_types.h b/libs/audiographer/src/gdither/gdither_types.h
new file mode 100644
index 0000000000..bcc0097d7f
--- /dev/null
+++ b/libs/audiographer/src/gdither/gdither_types.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef GDITHER_TYPES_H
+#define GDITHER_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ GDitherNone = 0,
+ GDitherRect,
+ GDitherTri,
+ GDitherShaped
+} GDitherType;
+
+typedef enum {
+ GDither8bit = 8,
+ GDither16bit = 16,
+ GDither32bit = 32,
+ GDitherFloat = 25,
+ GDitherDouble = 54
+} GDitherSize;
+
+typedef void *GDither;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libs/audiographer/src/gdither/gdither_types_internal.h b/libs/audiographer/src/gdither/gdither_types_internal.h
new file mode 100644
index 0000000000..6cb0c48af9
--- /dev/null
+++ b/libs/audiographer/src/gdither/gdither_types_internal.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef GDITHER_TYPES_H
+#define GDITHER_TYPES_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GDITHER_SH_BUF_SIZE 8
+#define GDITHER_SH_BUF_MASK 7
+
+/* this must agree with whats in gdither_types.h */
+typedef enum {
+ GDitherNone = 0,
+ GDitherRect,
+ GDitherTri,
+ GDitherShaped
+} GDitherType;
+
+typedef enum {
+ GDither8bit = 8,
+ GDither16bit = 16,
+ GDither32bit = 32,
+ GDitherFloat = 25,
+ GDitherDouble = 54
+} GDitherSize;
+
+typedef struct {
+ uint32_t phase;
+ float buffer[GDITHER_SH_BUF_SIZE];
+} GDitherShapedState;
+
+typedef struct GDither_s {
+ GDitherType type;
+ uint32_t channels;
+ uint32_t bit_depth;
+ uint32_t dither_depth;
+ float scale;
+ uint32_t post_scale;
+ float post_scale_fp;
+ float bias;
+
+ int clamp_u;
+
+ int clamp_l;
+ float *tri_state;
+ GDitherShapedState *shaped_state;
+} *GDither;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libs/audiographer/src/gdither/noise.h b/libs/audiographer/src/gdither/noise.h
new file mode 100644
index 0000000000..96a582ef9b
--- /dev/null
+++ b/libs/audiographer/src/gdither/noise.h
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2000-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef NOISE_H
+#define NOISE_H
+
+/* Can be overrriden with any code that produces whitenoise between 0.0f and
+ * 1.0f, eg (random() / (float)RAND_MAX) should be a good source of noise, but
+ * its expensive */
+#ifndef GDITHER_NOISE
+#define GDITHER_NOISE gdither_noise()
+#endif
+
+inline static float gdither_noise()
+{
+ static uint32_t rnd = 23232323;
+ rnd = (rnd * 196314165) + 907633515;
+
+ return rnd * 2.3283064365387e-10f;
+}
+
+#endif
diff --git a/libs/audiographer/src/routines.cc b/libs/audiographer/src/routines.cc
new file mode 100644
index 0000000000..b97653be75
--- /dev/null
+++ b/libs/audiographer/src/routines.cc
@@ -0,0 +1,7 @@
+#include "audiographer/routines.h"
+
+namespace AudioGrapher
+{
+Routines::compute_peak_t Routines::_compute_peak = &Routines::default_compute_peak;
+Routines::apply_gain_to_buffer_t Routines::_apply_gain_to_buffer = &Routines::default_apply_gain_to_buffer;
+}
diff --git a/libs/audiographer/src/sample_format_converter.cc b/libs/audiographer/src/sample_format_converter.cc
new file mode 100644
index 0000000000..5b2d3d6e8c
--- /dev/null
+++ b/libs/audiographer/src/sample_format_converter.cc
@@ -0,0 +1,190 @@
+#include "audiographer/sample_format_converter.h"
+
+#include "gdither/gdither.h"
+#include "audiographer/exception.h"
+
+#include <boost/format.hpp>
+
+#include <cstring>
+
+namespace AudioGrapher
+{
+
+template <typename TOut>
+SampleFormatConverter<TOut>::SampleFormatConverter (uint32_t channels) :
+ channels (channels),
+ dither (0),
+ data_out_size (0),
+ data_out (0),
+ clip_floats (false)
+{
+}
+
+template <>
+void
+SampleFormatConverter<float>::init (nframes_t max_frames, int type, int data_width)
+{
+ if (data_width != 32) { throw Exception (*this, "Unsupported data width"); }
+ init_common (max_frames);
+ dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
+}
+
+template <>
+void
+SampleFormatConverter<int32_t>::init (nframes_t max_frames, int type, int data_width)
+{
+ if(data_width < 24) { throw Exception (*this, "Use SampleFormatConverter<int16_t> for data widths < 24"); }
+
+ init_common (max_frames);
+
+ if (data_width == 24) {
+ dither = gdither_new ((GDitherType) type, channels, GDither32bit, data_width);
+ } else if (data_width == 32) {
+ dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
+ } else {
+ throw Exception (*this, "Unsupported data width");
+ }
+}
+
+template <>
+void
+SampleFormatConverter<int16_t>::init (nframes_t max_frames, int type, int data_width)
+{
+ if (data_width != 16) { throw Exception (*this, "Unsupported data width"); }
+ init_common (max_frames);
+ dither = gdither_new ((GDitherType) type, channels, GDither16bit, data_width);
+}
+
+template <>
+void
+SampleFormatConverter<uint8_t>::init (nframes_t max_frames, int type, int data_width)
+{
+ if (data_width != 8) { throw Exception (*this, "Unsupported data width"); }
+ init_common (max_frames);
+ dither = gdither_new ((GDitherType) type, channels, GDither8bit, data_width);
+}
+
+template <typename TOut>
+void
+SampleFormatConverter<TOut>::init_common (nframes_t max_frames )
+{
+ reset();
+ if (max_frames > data_out_size) {
+
+ delete[] data_out;
+
+ data_out = new TOut[max_frames];
+ data_out_size = max_frames;
+ }
+}
+
+template <typename TOut>
+SampleFormatConverter<TOut>::~SampleFormatConverter ()
+{
+ reset();
+}
+
+template <typename TOut>
+void
+SampleFormatConverter<TOut>::reset()
+{
+ if (dither) {
+ gdither_free (dither);
+ dither = 0;
+ }
+
+ delete[] data_out;
+ data_out_size = 0;
+ data_out = 0;
+
+ clip_floats = false;
+}
+
+/* Basic const version of process() */
+template <typename TOut>
+void
+SampleFormatConverter<TOut>::process (ProcessContext<float> const & c_in)
+{
+ float const * const data = c_in.data();
+ nframes_t const frames = c_in.frames();
+
+ check_frame_count (frames);
+
+ /* Do conversion */
+
+ for (uint32_t chn = 0; chn < channels; ++chn) {
+ gdither_runf (dither, chn, frames / channels, data, data_out);
+ }
+
+ /* Write forward */
+
+ ProcessContext<TOut> c_out(c_in, data_out);
+ output (c_out);
+}
+
+/* Basic non-const version of process(), calls the const one */
+template<typename TOut>
+void
+SampleFormatConverter<TOut>::process (ProcessContext<float> & c_in)
+{
+ process (static_cast<ProcessContext<float> const &> (c_in));
+}
+
+/* template specialization for float, in-place processing (non-const) */
+template<>
+void
+SampleFormatConverter<float>::process (ProcessContext<float> & c_in)
+{
+ nframes_t frames = c_in.frames();
+ float * data = c_in.data();
+
+ if (clip_floats) {
+ for (nframes_t x = 0; x < frames; ++x) {
+ if (data[x] > 1.0f) {
+ data[x] = 1.0f;
+ } else if (data[x] < -1.0f) {
+ data[x] = -1.0f;
+ }
+ }
+ }
+
+ output (c_in);
+}
+
+/* template specialized const version, copies the data, and calls the non-const version */
+template<>
+void
+SampleFormatConverter<float>::process (ProcessContext<float> const & c_in)
+{
+ // Make copy of data and pass it to non-const version
+ nframes_t frames = c_in.frames();
+ check_frame_count (frames);
+ memcpy (data_out, c_in.data(), frames * sizeof(float));
+
+ ProcessContext<float> c (c_in, data_out);
+ process (c);
+}
+
+template<typename TOut>
+void
+SampleFormatConverter<TOut>::check_frame_count(nframes_t frames)
+{
+ if (frames % channels != 0) {
+ throw Exception (*this, boost::str (boost::format (
+ "Number of frames given to process() was not a multiple of channels: %1% frames with %2% channels")
+ % frames % channels));
+ }
+
+ if (frames > data_out_size) {
+ throw Exception (*this, boost::str (boost::format (
+ "Too many frames given to process(), %1% instad of %2%")
+ % frames % data_out_size));
+ }
+}
+
+template class SampleFormatConverter<uint8_t>;
+template class SampleFormatConverter<int16_t>;
+template class SampleFormatConverter<int32_t>;
+template class SampleFormatConverter<float>;
+
+} // namespace
diff --git a/libs/audiographer/src/sndfile_base.cc b/libs/audiographer/src/sndfile_base.cc
new file mode 100644
index 0000000000..8d12f9341b
--- /dev/null
+++ b/libs/audiographer/src/sndfile_base.cc
@@ -0,0 +1,56 @@
+#include "audiographer/sndfile_base.h"
+#include "audiographer/exception.h"
+
+#include <boost/format.hpp>
+
+namespace AudioGrapher
+{
+
+using std::string;
+using boost::str;
+using boost::format;
+
+/* SndfileWriterBase */
+
+SndfileBase::SndfileBase (ChannelCount channels, nframes_t samplerate, int format, string const & path)
+ : path (path)
+{
+ char errbuf[256];
+
+ sf_info.channels = channels;
+ sf_info.samplerate = samplerate;
+ sf_info.format = format;
+
+ if (!sf_format_check (&sf_info)) {
+ throw Exception (*this, "Invalid format in constructor");
+ }
+
+ if (path.length() == 0) {
+ throw Exception (*this, "No output file specified");
+ }
+
+ /* TODO add checks that the directory path exists, and also
+ check if we are overwriting an existing file...
+ */
+
+ // Open file
+ if (path.compare ("temp")) {
+ if ((sndfile = sf_open (path.c_str(), SFM_WRITE, &sf_info)) == 0) {
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ throw Exception (*this, str (boost::format ("Cannot open output file \"%1%\" (%2%)") % path % errbuf));
+ }
+ } else {
+ FILE * file;
+ if (!(file = tmpfile ())) {
+ throw Exception (*this, "Cannot open tempfile");
+ }
+ sndfile = sf_open_fd (fileno(file), SFM_RDWR, &sf_info, true);
+ }
+}
+
+SndfileBase::~SndfileBase ()
+{
+ sf_close (sndfile);
+}
+
+} // namespace
diff --git a/libs/audiographer/src/sndfile_reader.cc b/libs/audiographer/src/sndfile_reader.cc
new file mode 100644
index 0000000000..0508110314
--- /dev/null
+++ b/libs/audiographer/src/sndfile_reader.cc
@@ -0,0 +1,67 @@
+#include "audiographer/sndfile_reader.h"
+
+#include <boost/format.hpp>
+
+#include "audiographer/exception.h"
+
+namespace AudioGrapher
+{
+
+template<typename T>
+SndfileReader<T>::SndfileReader (ChannelCount channels, nframes_t samplerate, int format, std::string path)
+ : SndfileBase (channels, samplerate, format, path)
+{
+ init ();
+}
+
+template<typename T>
+nframes_t
+SndfileReader<T>::seek (nframes_t frames, SeekType whence)
+{
+ return sf_seek (sndfile, frames, whence);
+}
+
+template<typename T>
+nframes_t
+SndfileReader<T>::read (ProcessContext<T> & context)
+{
+ if (context.channels() != sf_info.channels) {
+ throw Exception (*this, boost::str (boost::format (
+ "ProcessContext given to read() has a wrong amount of channels: %1% instead of %2%")
+ % context.channels() % sf_info.channels));
+ }
+
+ nframes_t frames_read = (*read_func) (sndfile, context.data(), context.frames());
+ if (frames_read < context.frames()) {
+ context.set_flag (ProcessContext<T>::EndOfInput);
+ }
+ output (context);
+ return frames_read;
+}
+
+template<>
+void
+SndfileReader<short>::init()
+{
+ read_func = &sf_read_short;
+}
+
+template<>
+void
+SndfileReader<int>::init()
+{
+ read_func = &sf_read_int;
+}
+
+template<>
+void
+SndfileReader<float>::init()
+{
+ read_func = &sf_read_float;
+}
+
+template class SndfileReader<short>;
+template class SndfileReader<int>;
+template class SndfileReader<float>;
+
+} // namespace \ No newline at end of file
diff --git a/libs/audiographer/src/sndfile_writer.cc b/libs/audiographer/src/sndfile_writer.cc
new file mode 100644
index 0000000000..d12d6b943b
--- /dev/null
+++ b/libs/audiographer/src/sndfile_writer.cc
@@ -0,0 +1,73 @@
+#include "audiographer/sndfile_writer.h"
+#include "audiographer/exception.h"
+
+#include <cstring>
+
+#include <boost/format.hpp>
+
+namespace AudioGrapher
+{
+
+using std::string;
+using boost::str;
+using boost::format;
+
+template <typename T>
+SndfileWriter<T>::SndfileWriter (ChannelCount channels, nframes_t samplerate, int format, string const & path) :
+ SndfileBase (channels, samplerate, format, path)
+{
+ // init write function
+ init ();
+}
+
+template <>
+void
+SndfileWriter<float>::init ()
+{
+ write_func = &sf_write_float;
+}
+
+template <>
+void
+SndfileWriter<int>::init ()
+{
+ write_func = &sf_write_int;
+}
+
+template <>
+void
+SndfileWriter<short>::init ()
+{
+ write_func = &sf_write_short;
+}
+
+template <typename T>
+void
+SndfileWriter<T>::process (ProcessContext<T> const & c)
+{
+ if (c.channels() != sf_info.channels) {
+ throw Exception (*this, str (boost::format(
+ "Wrong number of channels given to process(), %1% instead of %2%")
+ % c.channels() % sf_info.channels));
+ }
+
+ char errbuf[256];
+ nframes_t written = (*write_func) (sndfile, c.data(), c.frames());
+ if (written != c.frames()) {
+ sf_error_str (sndfile, errbuf, sizeof (errbuf) - 1);
+ throw Exception (*this, str ( format("Could not write data to output file (%1%)") % errbuf));
+ }
+
+ if (c.has_flag(ProcessContext<T>::EndOfInput)) {
+ sf_write_sync (sndfile);
+ //#ifdef HAVE_SIGCPP
+ FileWritten (path);
+ //#endif
+ }
+}
+
+template class SndfileWriter<short>;
+template class SndfileWriter<int>;
+template class SndfileWriter<float>;
+
+} // namespace
diff --git a/libs/audiographer/src/sr_converter.cc b/libs/audiographer/src/sr_converter.cc
new file mode 100644
index 0000000000..c61b3d0728
--- /dev/null
+++ b/libs/audiographer/src/sr_converter.cc
@@ -0,0 +1,218 @@
+#include "audiographer/sr_converter.h"
+#include "audiographer/exception.h"
+
+#include <cmath>
+#include <cstring>
+#include <boost/format.hpp>
+
+#define ENABLE_DEBUG 0
+
+#if ENABLE_DEBUG
+ #include <iostream>
+ #define DEBUG(str) std::cout << str << std::endl;
+#else
+ #define DEBUG(str)
+#endif
+
+namespace AudioGrapher
+{
+using boost::format;
+using boost::str;
+
+SampleRateConverter::SampleRateConverter (uint32_t channels)
+ : active (false)
+ , channels (channels)
+ , max_frames_in(0)
+ , leftover_data (0)
+ , leftover_frames (0)
+ , max_leftover_frames (0)
+ , data_out (0)
+ , data_out_size (0)
+ , src_state (0)
+{
+}
+
+void
+SampleRateConverter::init (nframes_t in_rate, nframes_t out_rate, int quality)
+{
+ reset();
+
+ if (in_rate == out_rate) {
+ src_data.src_ratio = 1;
+ return;
+ }
+
+ active = true;
+ int err;
+ if ((src_state = src_new (quality, channels, &err)) == 0) {
+ throw Exception (*this, str (format ("Cannot initialize sample rate converter: %1%") % src_strerror (err)));
+ }
+
+ src_data.src_ratio = (double) out_rate / (double) in_rate;
+}
+
+SampleRateConverter::~SampleRateConverter ()
+{
+ reset();
+}
+
+nframes_t
+SampleRateConverter::allocate_buffers (nframes_t max_frames)
+{
+ if (!active) { return max_frames; }
+
+ nframes_t max_frames_out = (nframes_t) ceil (max_frames * src_data.src_ratio);
+ if (data_out_size < max_frames_out) {
+
+ delete[] data_out;
+ data_out = new float[max_frames_out];
+ src_data.data_out = data_out;
+
+ max_leftover_frames = 4 * max_frames;
+ leftover_data = (float *) realloc (leftover_data, max_leftover_frames * sizeof (float));
+ if (!leftover_data) {
+ throw Exception (*this, "A memory allocation error occured");
+ }
+
+ max_frames_in = max_frames;
+ data_out_size = max_frames_out;
+ }
+
+ return max_frames_out;
+}
+
+void
+SampleRateConverter::process (ProcessContext<float> const & c)
+{
+ if (!active) {
+ output (c);
+ return;
+ }
+
+ nframes_t frames = c.frames();
+ float * in = const_cast<float *> (c.data()); // TODO check if this is safe!
+
+ if (frames > max_frames_in) {
+ throw Exception (*this, str (format (
+ "process() called with too many frames, %1% instead of %2%")
+ % frames % max_frames_in));
+ }
+
+ if (frames % channels != 0) {
+ throw Exception (*this, boost::str (boost::format (
+ "Number of frames given to process() was not a multiple of channels: %1% frames with %2% channels")
+ % frames % channels));
+ }
+
+ int err;
+ bool first_time = true;
+
+ do {
+ src_data.output_frames = data_out_size / channels;
+ src_data.data_out = data_out;
+
+ if (leftover_frames > 0) {
+
+ /* input data will be in leftover_data rather than data_in */
+
+ src_data.data_in = leftover_data;
+
+ if (first_time) {
+
+ /* first time, append new data from data_in into the leftover_data buffer */
+
+ memcpy (&leftover_data [leftover_frames * channels], in, frames * sizeof(float));
+ src_data.input_frames = frames + leftover_frames;
+ } else {
+
+ /* otherwise, just use whatever is still left in leftover_data; the contents
+ were adjusted using memmove() right after the last SRC call (see
+ below)
+ */
+
+ src_data.input_frames = leftover_frames;
+ }
+
+ } else {
+ src_data.data_in = in;
+ src_data.input_frames = frames / channels;
+ }
+
+ first_time = false;
+
+ DEBUG ("data_in: " << src_data.data_in);
+ DEBUG ("input_frames: " << src_data.input_frames);
+ DEBUG ("data_out: " << src_data.data_out);
+ DEBUG ("output_frames: " << src_data.output_frames);
+
+ if ((err = src_process (src_state, &src_data)) != 0) {
+ throw Exception (*this, str (format ("An error occured during sample rate conversion: %1%") % src_strerror (err)));
+ }
+
+ leftover_frames = src_data.input_frames - src_data.input_frames_used;
+
+ if (leftover_frames > 0) {
+ if (leftover_frames > max_leftover_frames) {
+ throw Exception(*this, "leftover frames overflowed");
+ }
+ memmove (leftover_data, (char *) &src_data.data_in[src_data.input_frames_used * channels],
+ leftover_frames * channels * sizeof(float));
+ }
+
+ ProcessContext<float> c_out (c, data_out, src_data.output_frames_gen * channels);
+ if (!src_data.end_of_input || leftover_frames) {
+ c_out.remove_flag (ProcessContext<float>::EndOfInput);
+ }
+ output (c_out);
+
+ DEBUG ("src_data.output_frames_gen: " << src_data.output_frames_gen << ", leftover_frames: " << leftover_frames);
+
+ if (src_data.output_frames_gen == 0 && leftover_frames) { throw Exception (*this, boost::str (boost::format (
+ "No output frames genereated with %1% leftover frames")
+ % leftover_frames)); }
+
+ } while (leftover_frames > frames);
+
+ // src_data.end_of_input has to be checked to prevent infinite recursion
+ if (!src_data.end_of_input && c.has_flag(ProcessContext<float>::EndOfInput)) {
+ set_end_of_input (c);
+ }
+}
+
+void SampleRateConverter::set_end_of_input (ProcessContext<float> const & c)
+{
+ src_data.end_of_input = true;
+
+ float f;
+ ProcessContext<float> const dummy (c, &f, 0, channels);
+
+ /* No idea why this has to be done twice for all data to be written,
+ * but that just seems to be the way it is...
+ */
+ process (dummy);
+ process (dummy);
+}
+
+
+void SampleRateConverter::reset ()
+{
+ active = false;
+ max_frames_in = 0;
+ src_data.end_of_input = false;
+
+ if (src_state) {
+ src_delete (src_state);
+ }
+
+ leftover_frames = 0;
+ max_leftover_frames = 0;
+ if (leftover_data) {
+ free (leftover_data);
+ }
+
+ data_out_size = 0;
+ delete [] data_out;
+ data_out = 0;
+}
+
+} // namespace
diff --git a/libs/audiographer/src/utils.cc b/libs/audiographer/src/utils.cc
new file mode 100644
index 0000000000..018fad3113
--- /dev/null
+++ b/libs/audiographer/src/utils.cc
@@ -0,0 +1,14 @@
+#include "audiographer/utils.h"
+
+using namespace AudioGrapher;
+
+char const * Utils::zeros = 0;
+unsigned long Utils::num_zeros = 0;
+
+void
+Utils::free_resources()
+{
+ num_zeros = 0;
+ delete [] zeros;
+ zeros = 0;
+} \ No newline at end of file