Re-integrate export-optimization branch.

Export now happens directly to file (unless normalizing is required), and can be easily optimized even further.
The Session process connection is still broken during export (as it was before this commit also).


git-svn-id: svn://localhost/ardour2/branches/3.0@6401 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Sakari Bergen
2009-12-27 14:46:23 +00:00
parent 35c72a53b4
commit dde0848a98
81 changed files with 5370 additions and 2214 deletions

View File

@@ -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: */

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}