// ------------------------------------------------------------------------
// samplebuffer.cpp: Class representing a buffer of audio samples.
// Copyright (C) 1999-2005,2009 Kai Vehmanen
//
// Attributes:
//     eca-style-version: 3
//
// References:
//   - libsamplerate:
//     http://www.mega-nerd.com/SRC/
//   - liboil:
//     http://liboil.freedesktop.org/
//     http://library.gnome.org/devel/liboil/
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
// ------------------------------------------------------------------------

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <iostream>
#include <vector>

#include <cmath>    /* ceil(), floor() */
#include <cstring>  /* memcpy */
#include <stdlib.h> /* not cstdlib we need e.g. posix_memalign() */

#include <sys/types.h>

#include <kvu_dbc.h>
#include <kvu_numtostr.h>

#ifdef ECA_COMPILE_SAMPLERATE
#include <samplerate.h>
#endif

#ifdef ECA_USE_LIBOIL 
extern "C" {
#include <liboil/liboil.h>
}
#endif

#include "eca-sample-conversion.h"
#include "samplebuffer.h"
#include "samplebuffer_impl.h"
#include "eca-logger.h"

/* Debug resampling operations */ 
// #define DEBUG_RESAMPLING

#ifdef DEBUG_RESAMPLING
#define DEBUG_RESAMPLING_STATEMENT(x) x
#else
#define DEBUG_RESAMPLING_STATEMENT(x) ((void)0)
#endif

#ifdef WORDS_BIGENDIAN
static const bool is_system_littleendian = false;
#else
static const bool is_system_littleendian = true;
#endif

using namespace std;

static void priv_alloc_sample_buf(SAMPLE_SPECS::sample_t **memptr, size_t size)
{
#ifdef HAVE_POSIX_MEMALIGN
  /* align buffers to 128bit/16octet boundary */
  posix_memalign(reinterpret_cast<void**>(memptr), 16, size);
#else
  *memptr = reinterpret_cast<SAMPLE_SPECS::sample_t*>(malloc(size));
#endif

}

/**
 * Constructs a new sample buffer object.
 */
SAMPLE_BUFFER::SAMPLE_BUFFER (buf_size_t buffersize, channel_size_t channels)
  : channel_count_rep(channels),
    buffersize_rep(buffersize),
    reserved_samples_rep(buffersize) 
{
  // ---
  DBC_REQUIRE(buffersize >= 0);
  DBC_REQUIRE(channels >= 0);
  // ---

  /* catch if someone changes SAMPLE_SPECS::silent_value */
  DBC_DECLARE(float f = 0.0f);
  DBC_CHECK(static_cast<uint32_t>(f) == 0);
  DBC_CHECK(sizeof(f) == 4);

  impl_repp = new SAMPLE_BUFFER_impl;

  buffer.resize(channels);
  for(size_t n = 0; n < buffer.size(); n++) {
    priv_alloc_sample_buf(&buffer[n], 
			  sizeof(sample_t) * reserved_samples_rep);
  }
  make_silent();

  impl_repp->rt_lock_rep = false;
  impl_repp->lockref_rep = 0;
  impl_repp->old_buffer_repp = 0;
#ifdef ECA_COMPILE_SAMPLERATE
  impl_repp->quality_rep = 50;
  impl_repp->src_state_rep.resize(channels);
#else
  impl_repp->quality_rep = 5;
#endif

#ifdef ECA_USE_LIBOIL
  oil_init();
#endif
 
  ECA_LOG_MSG(ECA_LOGGER::functions, 
		"Buffer created, channels: " +
		kvu_numtostr(buffer.size()) + ", length-samples: " +
		kvu_numtostr(buffersize_rep) + ".");

  // ---
  DBC_ENSURE(buffer.size() == static_cast<size_t>(channel_count_rep));
  // ---
}

/**
 * Destructor.
 */
SAMPLE_BUFFER::~SAMPLE_BUFFER (void)
{
  DBC_CHECK(impl_repp->lockref_rep == 0);

  for(size_t n = 0; n < buffer.size(); n++) {
    if (buffer[n] != 0) {
      ::free(buffer[n]);
      buffer[n] = 0;
    }
  }

  if (impl_repp->old_buffer_repp != 0) {
    ::free(impl_repp->old_buffer_repp);
    impl_repp->old_buffer_repp = 0;
  }

#ifdef ECA_COMPILE_SAMPLERATE
  for(size_t n = 0; n < impl_repp->src_state_rep.size(); n++) {
    if (impl_repp->src_state_rep[n] != 0) {
      src_delete(impl_repp->src_state_rep[n]);
      impl_repp->src_state_rep[n] = 0;
    }
  }
#endif

  delete impl_repp;
}

/**
 * Channel-wise addition. Buffer length is increased if necessary.
 * Only channels, that are present in both source and destination, are 
 * modified.
 *
 * Note: event tags are not copied!
 * 
 * @post length_in_samples() >= x.length_in_samples()
 */
void SAMPLE_BUFFER::add_matching_channels(const SAMPLE_BUFFER& x)
{
#ifdef ECA_USE_LIBOIL 
  if (x.length_in_samples() > length_in_samples()) {
    length_in_samples(x.length_in_samples());
  }
  int min_c_count = (channel_count_rep <= x.channel_count_rep) ? channel_count_rep : x.channel_count_rep;
  for(channel_size_t q = 0; q < min_c_count; q++) {
    oil_add_f32(reinterpret_cast<float*>(buffer[q]),
		reinterpret_cast<const float*>(buffer[q]),
		reinterpret_cast<const float*>(x.buffer[q]),
		x.length_in_samples());
  }
#else
  add_matching_channels_ref(x);
#endif
}

/**
 * Unoptimized version of add_matching_channels().
 */
void SAMPLE_BUFFER::add_matching_channels_ref(const SAMPLE_BUFFER& x)
{
  if (x.length_in_samples() > length_in_samples()) {
    length_in_samples(x.length_in_samples());
  }
  int min_c_count = (channel_count_rep <= x.channel_count_rep) ? channel_count_rep : x.channel_count_rep;
  for(channel_size_t q = 0; q < min_c_count; q++) {
    for(buf_size_t t = 0; t < x.length_in_samples(); t++) {
      buffer[q][t] += x.buffer[q][t];
    }
  }
}

/**
 * Channel-wise, weighted addition. Before addition every sample is 
 * multiplied by '1/weight'. Buffer length is increased if necessary.
 * Only channels, that are present in both source and destination, are 
 * modified.
 *
 * Note: event tags are not copied!
 * 
 * @pre weight != 0
 * @post length_in_samples() >= x.length_in_samples()
 */
void SAMPLE_BUFFER::add_with_weight(const SAMPLE_BUFFER& x, int weight)
{
  // ---
  DBC_REQUIRE(weight != 0);
  // ---

  /* note: gcc does a suprisingly good job for this function,
   *       so additional optimizations don't seem worthwhile */

  if (x.length_in_samples() > length_in_samples()) {
    length_in_samples(x.length_in_samples());
  }
  int min_c_count = (channel_count_rep <= x.channel_count_rep) ? channel_count_rep : x.channel_count_rep;
  for(channel_size_t q = 0; q < min_c_count; q++) {
    for(buf_size_t t = 0; t < x.length_in_samples(); t++) {
      buffer[q][t] += (x.buffer[q][t] / weight);
    }
  }
}

/**
 * Channel-wise copy. Buffer length is adjusted if necessary.
 *
 * Note: event tags are not copied!
 * 
 * @post length_in_samples() == x.length_in_samples()
 */
void SAMPLE_BUFFER::copy_matching_channels(const SAMPLE_BUFFER& x)
{
  length_in_samples(x.length_in_samples());
  
  int min_c_count = (channel_count_rep <= x.channel_count_rep) ? channel_count_rep : x.channel_count_rep;
  for(channel_size_t q = 0; q < min_c_count; q++) {
    std::memcpy(buffer[q], 
		x.buffer[q],
		sizeof(sample_t) * length_in_samples());

    /* ref implementation:
     * for(buf_size_t t = 0; t < length_in_samples(); t++) {
     *   buffer[q][t] = x.buffer[q][t];
     * }
     */ 
  }
}

/**
 * Copy all audio contents from 'x'. After copying, length, channel
 * count, and event tag set match to those of 'x'.
 *
 * @post length_in_samples() == x.length_in_samples()
 * @post number_of_channels() == number_of_channels()
 * @post for all I: event_tag_test(I) == x.event_tag_test(I)
 */
void SAMPLE_BUFFER::copy_all_content(const SAMPLE_BUFFER& x)
{
  length_in_samples(x.length_in_samples());
  number_of_channels(x.number_of_channels());
  
  for(channel_size_t q = 0; q < number_of_channels(); q++) {
    std::memcpy(buffer[q], 
		x.buffer[q],
		sizeof(sample_t) * length_in_samples());

    /* ref implementation:
     * {
     * for(buf_size_t t = 0; t < length_in_samples(); t++) {
     * 	buffer[q][t] = x.buffer[q][t];
     * }
     */
  }
  
  event_tags_set(x);
}

/**
 * Ranged channel-wise copy. Copies samples in range 
 * 'start_pos' - 'end_pos-1' from buffer 'x' to current 
 * buffer position 'to_pos'. The 'src' object must have
 * equal number of channels as the current object.
 *
 * Note: event tags are not copied!
 * 
 * @pre start_pos <= end_pos
 * @pre 
 * @pre to_pos < length_in_samples()
 */
void SAMPLE_BUFFER::copy_range(const SAMPLE_BUFFER& src, 
			       buf_size_t src_start_pos,
			       buf_size_t src_end_pos,
			       buf_size_t dst_to_pos) 
{
  // ---
  DBC_REQUIRE(src_start_pos <= src_end_pos);
  DBC_REQUIRE(dst_to_pos < length_in_samples());
  DBC_REQUIRE(number_of_channels() == src.number_of_channels());
  // ---

  if (src_end_pos > src.length_in_samples())
    src_end_pos = src.length_in_samples();

  for(channel_size_t q = 0; q < channel_count_rep; q++) {
    buf_size_t dst_i = dst_to_pos;
    for(buf_size_t src_i = src_start_pos; 
	  src_i < src_end_pos && 
	  dst_i < length_in_samples();
	src_i++, dst_i++) {

      buffer[q][dst_i] = src.buffer[q][src_i];

#ifdef SAMPLEBUFFER_COPY_RANGE_VERBOSE_DEBUG
      if (dst_i == 0 || src_i == 0 || src_i == src_end_pos - 1 ||dst_i == length_in_samples() - 1) {
	std::fprintf(stderr, "dst[%d][%ld] = src[%d][%ld]\n", 
		     q, dst_i, q, src_i);
      }
#endif
    }
  }
}

void SAMPLE_BUFFER::multiply_by(SAMPLE_BUFFER::sample_t factor, int channel)
{
#ifdef ECA_USE_LIBOIL 
  oil_scalarmultiply_f32_ns(reinterpret_cast<float*>(buffer[channel]), 
			    reinterpret_cast<const float*>(buffer[channel]), 
			    reinterpret_cast<const float*>(&factor), 
			    buffersize_rep);
#else
  multiply_by_ref(factor, channel);
#endif
}

void SAMPLE_BUFFER::multiply_by(SAMPLE_BUFFER::sample_t factor)
{
  for(channel_size_t n = 0; n < channel_count_rep; n++) {
    multiply_by(factor, n);
  }
}

void SAMPLE_BUFFER::multiply_by_ref(SAMPLE_BUFFER::sample_t factor)
{
  for(channel_size_t n = 0; n < channel_count_rep; n++) {
    for(buf_size_t m = 0; m < buffersize_rep; m++) {
      buffer[n][m] *= factor;
    }
  }
}

void SAMPLE_BUFFER::multiply_by_ref(SAMPLE_BUFFER::sample_t factor, int channel)
{
  for(buf_size_t m = 0; m < buffersize_rep; m++) {
    buffer[channel][m] *= factor;
  }
}

/**
 * Divides all samples by 'dvalue'.
 */
void SAMPLE_BUFFER::divide_by(SAMPLE_BUFFER::sample_t dvalue)
{
  multiply_by(1.0 / dvalue);
}

void SAMPLE_BUFFER::divide_by_ref(SAMPLE_BUFFER::sample_t dvalue)
{
  multiply_by_ref(1.0 / dvalue);
}

/**
 * Clears the buffer to zero length. Note that this is
 * different from a silent buffer.
 */
void SAMPLE_BUFFER::make_empty(void)
{
  SAMPLE_BUFFER::length_in_samples(0);
}

/**
 * Mutes one channel  of the buffer.
 *
 * @pre channel >= 0 
 * @pre channel < number_of_channels()
 */
void SAMPLE_BUFFER::make_silent(int channel)
{
  DBC_REQUIRE(channel >= 0);
  DBC_REQUIRE(channel < number_of_channels());

  std::memset(buffer[channel], 0, buffersize_rep * sizeof(SAMPLE_SPECS::silent_value));

  /* - the generic version: */
  /*
    for(buf_size_t s = 0; s < buffersize_rep; s++) {
    buffer[n][s] = SAMPLE_SPECS::silent_value;
    }
  */
}

/**
 * Unoptimized version of make_silent(int).
 */
void SAMPLE_BUFFER::make_silent_ref(int channel)
{
  for(buf_size_t s = 0; s < buffersize_rep; s++) {
    buffer[channel][s] = SAMPLE_SPECS::silent_value;
  }
}

/**
 * Mutes the whole buffer.
 */
void SAMPLE_BUFFER::make_silent(void)
{
  for(channel_size_t n = 0; n < channel_count_rep; n++) {
    make_silent(n);
  }
}

/**
 * Mute a range of samples.
 * 
 * @pre start_pos >= 0
 * @pre end_pos >= 0
 */
void SAMPLE_BUFFER::make_silent_range(buf_size_t start_pos,
				      buf_size_t end_pos) {
  // --
  DBC_REQUIRE(start_pos >= 0);
  DBC_REQUIRE(end_pos >= 0);
  // --

  for(channel_size_t n = 0; n < channel_count_rep; n++) {
    std::memset(buffer[n] + start_pos, 
		0,
		sizeof(sample_t) * 
		(end_pos < buffersize_rep ? end_pos : buffersize_rep));
  }
}

/**
 * Unoptimized version of make_silent_range()
 */
void SAMPLE_BUFFER::make_silent_range_ref(buf_size_t start_pos,
					  buf_size_t end_pos) {
  // --
  DBC_REQUIRE(start_pos >= 0);
  DBC_REQUIRE(end_pos >= 0);
  // --

  for(channel_size_t n = 0; n < channel_count_rep; n++) {
    for(buf_size_t s = start_pos; s < end_pos && s < buffersize_rep; s++) {
      buffer[n][s] = SAMPLE_SPECS::silent_value;
    }
  }
}

/**
 * Limits all samples to valid values. 
 */
void SAMPLE_BUFFER::limit_values(void)
{
  for(channel_size_t n = 0; n < channel_count_rep; n++) {
    for(buf_size_t m = 0; m < buffersize_rep; m++) {
      if (buffer[n][m] > SAMPLE_SPECS::impl_max_value) 
	buffer[n][m] = SAMPLE_SPECS::impl_max_value;
      else if (buffer[n][m] < SAMPLE_SPECS::impl_min_value) 
	buffer[n][m] = SAMPLE_SPECS::impl_min_value;
    }
  }
  
#if 0 /* slower than the naive implementation */
  {
    float min = SAMPLE_SPECS::impl_min_value;
    float max = SAMPLE_SPECS::impl_max_value;
    for(channel_size_t n = 0; n < channel_count_rep; n++) {
      oil_clip_f32(reinterpret_cast<float*>(buffer[n]),
		   sizeof(float),
		   reinterpret_cast<const float*>(buffer[n]),
		   sizeof(float),
		   buffersize_rep,
		   &min, &max);
    }
  }
#endif
}

/** Unoptimized version of limit_values() */
void SAMPLE_BUFFER::limit_values_ref(void)
{
  limit_values();
}

/**
 * Resamples samplebuffer contents. Resampling
 * changes buffer length by 'to_rate/from_rate'.
 *
 * @post to_rate / from_rate * old_length_in_samples - length_in_samples() >= -1
 */
void SAMPLE_BUFFER::resample(SAMPLE_SPECS::sample_rate_t from_rate,
			     SAMPLE_SPECS::sample_rate_t to_rate)
{
#ifndef ECA_COMPILE_SAMPLERATE
  DBC_DECLARE(buf_size_t old_length_in_samples = length_in_samples());
#endif

#ifdef ECA_COMPILE_SAMPLERATE
  if (impl_repp->quality_rep > 5) {
    resample_secret_rabbit_code(from_rate, to_rate);
  }
  else 
#endif
    {
      DBC_CHECK(impl_repp->quality_rep <= 5);
      resample_with_memory(from_rate, to_rate); 
    }

#ifndef ECA_COMPILE_SAMPLERATE
  /* with libsamplerate, the output sample count can vary from call to call */
  DBC_CHECK((static_cast<double>(to_rate) / from_rate * old_length_in_samples - length_in_samples()) >= -1);
#endif
}

/**
 * Set resampling quality. 
 *
 * Depending on build options, not all quality levels
 * are necessarily supported. If the requested quality level 
 * exceeds the available resamplers, the quality setting 
 * is set to the highest available algorithm. You can use
 * the get_resample_quality() function to query the current level.
 *
 * @param quality value between 0 (lowest) to 100 (highest)
 */
void SAMPLE_BUFFER::resample_set_quality(int quality)
{
#ifdef ECA_COMPILE_SAMPLERATE
  impl_repp->quality_rep = quality;
#else
  if (quality > 10) {
    ECA_LOG_MSG(ECA_LOGGER::info, 
		"WARNING: Libsamplerate is required for high-quality resampling. "
		"Using the internal resampler instead.");
    impl_repp->quality_rep = 5;
  }
#endif
}

/**
 * Returns current resampling quality.
 *
 * @param value between 0 (lowest) to 100 (highest)
 */
int SAMPLE_BUFFER::resample_get_quality(void) const
{
  return impl_repp->quality_rep;
}

void SAMPLE_BUFFER::export_helper(unsigned char* obuffer, 
				  buf_size_t* optr,
				  sample_t value,
				  ECA_AUDIO_FORMAT::Sample_format fmt)

{
  switch (fmt) {
  case ECA_AUDIO_FORMAT::sfmt_u8:
    {
      obuffer[(*optr)++] = eca_sample_convert_float_to_u8(value);
      break;
    }
    
  case ECA_AUDIO_FORMAT::sfmt_s16_le:
    {
      int16_t s16temp = eca_sample_convert_float_to_s16(value);

      // little endian: (LSB, MSB) (Intel).
      obuffer[(*optr)++] = (unsigned char)(s16temp & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s16temp >> 8) & 0xff);
      break;
    }
      
  case ECA_AUDIO_FORMAT::sfmt_s16_be:
    {
      int16_t s16temp = eca_sample_convert_float_to_s16(value);
      
      // big endian: (MSB, LSB) (Motorola).
      obuffer[(*optr)++] = (unsigned char)((s16temp >> 8) & 0xff);
      obuffer[(*optr)++] = (unsigned char)(s16temp & 0xff);
      break;
    }
    
  case ECA_AUDIO_FORMAT::sfmt_s24_le:
    {
      int32_t s32temp = eca_sample_convert_float_to_s32(value);
      
      /* skip the LSB-byte of s32temp (s32temp & 0xff) */
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 8) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 16) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 24) & 0xff);	
      break;
    }
    
  case ECA_AUDIO_FORMAT::sfmt_s24_be:
    {
      int32_t s32temp = eca_sample_convert_float_to_s32(value);
      
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 24) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 16) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 8) & 0xff);
      /* skip the LSB-byte of s32temp (s32temp & 0xff) */
      break;
    }
    
  case ECA_AUDIO_FORMAT::sfmt_s32_le:
    {
      int32_t s32temp = eca_sample_convert_float_to_s32(value);
      
      obuffer[(*optr)++] = (unsigned char)(s32temp & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 8) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 16) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 24) & 0xff);
      break;
    }
    
  case ECA_AUDIO_FORMAT::sfmt_s32_be:
    {
      int32_t s32temp = eca_sample_convert_float_to_s32(value);
      
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 24) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 16) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((s32temp >> 8) & 0xff);
      obuffer[(*optr)++] = (unsigned char)(s32temp & 0xff);
      break;
    }
    
  case ECA_AUDIO_FORMAT::sfmt_f32_le:
    {
      union { int32_t i; float f; } f32temp;
      f32temp.f = (float)value;
      obuffer[(*optr)++] = (unsigned char)(f32temp.i & 0xff);
      obuffer[(*optr)++] = (unsigned char)((f32temp.i >> 8) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((f32temp.i >> 16) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((f32temp.i >> 24) & 0xff);
      break;
    }
    
  case ECA_AUDIO_FORMAT::sfmt_f32_be:
    {
      union { int32_t i; float f; } f32temp;
      f32temp.f = (float)value;
      obuffer[(*optr)++] = (unsigned char)((f32temp.i >> 24) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((f32temp.i >> 16) & 0xff);
      obuffer[(*optr)++] = (unsigned char)((f32temp.i >> 8) & 0xff);
      obuffer[(*optr)++] = (unsigned char)(f32temp.i & 0xff);
      break;
    }
    
  default: 
    { 
      ECA_LOG_MSG(ECA_LOGGER::info, "Unknown sample format! [1].");
    }
  }
}

/**
 * Exports contents of sample buffer to 'target'. Sample data 
 * will be converted according to the given arguments
 * (sample format and endianess). If 'chcount > 1', channels 
 * will be written interleaved.
 *
 * Note! If chcount > number_of_channels(), empty
 *       channels will be automatically added.
 *
 * @pre target != 0
 * @pre chcount > 0
 * @ensure number_of_channels() >= chcount
 */
void SAMPLE_BUFFER::export_interleaved(unsigned char* target,
				       ECA_AUDIO_FORMAT::Sample_format fmt,
				       channel_size_t chcount) 
{
  // --------
  DBC_REQUIRE(target != 0);
  DBC_REQUIRE(chcount > 0);
  // --------

  if (chcount > channel_count_rep) number_of_channels(chcount);

  buf_size_t osize = 0;
  for(buf_size_t isize = 0; isize < buffersize_rep; isize++) {
    for(channel_size_t c = 0; c < chcount; c++) {
      sample_t stemp = buffer[c][isize];
      if (stemp > SAMPLE_SPECS::impl_max_value) stemp = SAMPLE_SPECS::impl_max_value;
      else if (stemp < SAMPLE_SPECS::impl_min_value) stemp = SAMPLE_SPECS::impl_min_value;

      SAMPLE_BUFFER::export_helper(target, &osize, stemp, fmt);
    }
  }
  
  // -------
  DBC_ENSURE(number_of_channels() >= chcount);
  // -------
}

/**
 * Same as 'export_data()', but 'target' data is 
 * written in non-interleaved format.
 *
 * Note! If chcount > number_of_channels(), empty
 *       channels will be automatically added.
 *
 * @pre target != 0
 * @pre chcount > 0
 * @ensure number_of_channels() >= chcount
 */
void SAMPLE_BUFFER::export_noninterleaved(unsigned char* target,
					  ECA_AUDIO_FORMAT::Sample_format fmt,
					  channel_size_t chcount)
{
  // --------
  DBC_REQUIRE(target != 0);
  DBC_REQUIRE(chcount > 0);
  // --------

  if (chcount > channel_count_rep) number_of_channels(chcount);

  buf_size_t osize = 0;
  for(channel_size_t c = 0; c < chcount; c++) {
    for(buf_size_t isize = 0; isize < buffersize_rep; isize++) {
      sample_t stemp = buffer[c][isize];
      if (stemp > SAMPLE_SPECS::impl_max_value) stemp = SAMPLE_SPECS::impl_max_value;
      else if (stemp < SAMPLE_SPECS::impl_min_value) stemp = SAMPLE_SPECS::impl_min_value;
      
      SAMPLE_BUFFER::export_helper(target, &osize, stemp, fmt);
    }
  }

  // -------
  DBC_ENSURE(number_of_channels() >= chcount);
  // -------
}

void SAMPLE_BUFFER::import_helper(const unsigned char *ibuffer,
				  buf_size_t* iptr,
				  sample_t* obuffer,
				  buf_size_t optr,
				  ECA_AUDIO_FORMAT::Sample_format fmt)
{
  unsigned char a[2];
  unsigned char b[4];

  switch (fmt) {
  case ECA_AUDIO_FORMAT::sfmt_u8: 
    {
      obuffer[optr] = eca_sample_convert_u8_to_float(ibuffer[(*iptr)++]);
    }
    break;
	
  case ECA_AUDIO_FORMAT::sfmt_s16_le:
    {
      // little endian: (LSB, MSB) (Intel)
      // big endian: (MSB, LSB) (Motorola)
      if (is_system_littleendian) {
	a[0] = ibuffer[(*iptr)++];
	a[1] = ibuffer[(*iptr)++];
      }
      else {
	a[1] = ibuffer[(*iptr)++];
	a[0] = ibuffer[(*iptr)++];
      }
      int16_t tmp;
      memcpy(&tmp, a, sizeof(tmp));
      obuffer[optr] = eca_sample_convert_s16_to_float(tmp);
    }
    break;

  case ECA_AUDIO_FORMAT::sfmt_s16_be:
    {
      if (!is_system_littleendian) {
	a[0] = ibuffer[(*iptr)++];
	a[1] = ibuffer[(*iptr)++];
      }
      else {
	a[1] = ibuffer[(*iptr)++];
	a[0] = ibuffer[(*iptr)++];
      }
      int16_t tmp;
      memcpy(&tmp, a, sizeof(tmp));
      obuffer[optr] = eca_sample_convert_s16_to_float(tmp);
    }
    break;

  case ECA_AUDIO_FORMAT::sfmt_s24_le:
    {
      if (is_system_littleendian) {
	b[0] = 0; /* LSB */
	b[1] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[3] = ibuffer[(*iptr)++];
      }
      else {
	b[3] = 0; /* LSB */
	b[2] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[0] = ibuffer[(*iptr)++];
      }
      int32_t tmp;
      memcpy(&tmp, b, sizeof(tmp));
      obuffer[optr] = eca_sample_convert_s32_to_float(tmp);
    }
    break;

  case ECA_AUDIO_FORMAT::sfmt_s24_be:
    {
      if (is_system_littleendian) {
	b[3] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[0] = 0; /* LSB */
      }
      else {
	b[0] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[3] = 0; /* LSB */
      }
      int32_t tmp;
      memcpy(&tmp, b, sizeof(tmp));
      obuffer[optr] = eca_sample_convert_s32_to_float(tmp);
    }
    break;

  case ECA_AUDIO_FORMAT::sfmt_s32_le:
    {
      if (is_system_littleendian) {
	b[0] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[3] = ibuffer[(*iptr)++];
      }
      else {
	b[3] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[0] = ibuffer[(*iptr)++];
      }
      int32_t tmp;
      memcpy(&tmp, b, sizeof(tmp));
      obuffer[optr] = eca_sample_convert_s32_to_float(tmp);
    }
    break;

  case ECA_AUDIO_FORMAT::sfmt_s32_be:
    {
      if (is_system_littleendian) {
	b[3] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[0] = ibuffer[(*iptr)++];
      }
      else {
	b[0] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[3] = ibuffer[(*iptr)++];
      }
      int32_t tmp;
      memcpy(&tmp, b, sizeof(tmp));
      obuffer[optr] = eca_sample_convert_s32_to_float(tmp);
    }
    break;

  case ECA_AUDIO_FORMAT::sfmt_f32_le:
    {
      if (is_system_littleendian) {
	b[0] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[3] = ibuffer[(*iptr)++];
      }
      else {
	b[3] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[0] = ibuffer[(*iptr)++];
      }
      memcpy(&obuffer[optr], b, sizeof(obuffer[optr]));
    }
    break;

  case ECA_AUDIO_FORMAT::sfmt_f32_be:
    {
      if (is_system_littleendian) {
	b[3] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[0] = ibuffer[(*iptr)++];
      }
      else {
	b[0] = ibuffer[(*iptr)++];
	b[1] = ibuffer[(*iptr)++];
	b[2] = ibuffer[(*iptr)++];
	b[3] = ibuffer[(*iptr)++];
      }
      memcpy(&obuffer[optr], b, sizeof(obuffer[optr]));
    }
    break;

  default: 
    { 
      ECA_LOG_MSG(ECA_LOGGER::info, "Unknown sample format! [4].");
    }
  }
}

/**
 * Import audio from external raw buffer. Sample data 
 * will be converted to internal sample format using the 
 * given arguments (sample format and endianess). 
 * Channels will be read interleaved.
 *
 * @pre source != 0
 * @pre samples_read >= 0
 */
void SAMPLE_BUFFER::import_interleaved(unsigned char* source,
				       buf_size_t samples_read,
				       ECA_AUDIO_FORMAT::Sample_format fmt,
				       channel_size_t chcount)
{
  // --------
  DBC_REQUIRE(source != 0);
  DBC_REQUIRE(samples_read >= 0);
  // --------

  if (channel_count_rep != chcount) number_of_channels(chcount);
  if (buffersize_rep != samples_read) length_in_samples(samples_read);

  buf_size_t isize = 0;

  for(buf_size_t osize = 0; osize < buffersize_rep; osize++) {
    for(channel_size_t c = 0; c < chcount; c++) {
      import_helper(source, &isize, buffer[c], osize, fmt);
    }
  }
}

/**
 * Same as 'import_interleaved()', but 'source' data is 
 * assumed to be in non-interleaved format.
 *
 * @pre source != 0
 * @pre samples_read >= 0
 */
void SAMPLE_BUFFER::import_noninterleaved(unsigned char* source,
					  buf_size_t samples_read,
					  ECA_AUDIO_FORMAT::Sample_format fmt,
					  channel_size_t chcount)
{
  // --------
  DBC_REQUIRE(source != 0);
  DBC_REQUIRE(samples_read >= 0);
  // --------

  if (channel_count_rep != chcount) number_of_channels(chcount);
  if (buffersize_rep != samples_read) length_in_samples(samples_read);

  buf_size_t isize = 0;
  for(channel_size_t c = 0; c < chcount; c++) {
    for(buf_size_t osize = 0; osize < buffersize_rep; osize++) {
      import_helper(source, &isize, buffer[c], osize, fmt);
    }
  }
}

/** 
 * Sets the number of audio channels.
 */
void SAMPLE_BUFFER::number_of_channels(channel_size_t len) 
{
  // std::cerr << "(samplebuffer_impl) ch-count changes from " << channel_count_rep << " to " << len << ".\n";

  if (len > static_cast<channel_size_t>(buffer.size())) {
    DBC_CHECK(impl_repp->rt_lock_rep != true);

    size_t old_size = buffer.size();
    buffer.resize(len);
    for(channel_size_t n = old_size; n < len; n++) {
      priv_alloc_sample_buf(&buffer[n], sizeof(sample_t) * reserved_samples_rep);
    }
    ECA_LOG_MSG(ECA_LOGGER::functions, "Increasing channel-count (1).");    
  }

  /* note! channel_count_rep and buffer.size() necessarily
   *       weren't the same before this call, so we need
   *       to double check for old data
   */
  if (len > channel_count_rep) {
    for(channel_size_t n = channel_count_rep; n < len; n++) {
      for(buf_size_t m = 0; m < reserved_samples_rep; m++) {
	buffer[n][m] = SAMPLE_SPECS::silent_value;
      }
    }
    // ECA_LOG_MSG(ECA_LOGGER::system_objects, "Increasing channel-count (2).");
  }

  channel_count_rep = len;
}

/**
 * Sets the length of buffer in samples.
 *
 * Note: if length is increased, the added samples
 *       are muted.
 */
void SAMPLE_BUFFER::length_in_samples(buf_size_t len)
{
  DBC_REQUIRE(len >= 0);
  DBC_CHECK(buffersize_rep <= reserved_samples_rep);

  if (len > reserved_samples_rep) {

    DBC_CHECK(impl_repp->rt_lock_rep != true);
    DBC_CHECK(impl_repp->lockref_rep == 0);

    reserved_samples_rep = len * 2;
    for(size_t n = 0; n < buffer.size(); n++) {
      sample_t *prev_buffer = buffer[n];
      priv_alloc_sample_buf(&buffer[n], sizeof(sample_t) * reserved_samples_rep);
      for (buf_size_t m = 0; m < buffersize_rep; m++)
	buffer[n][m] = prev_buffer[m];
      ::free(prev_buffer);
    }

    if (impl_repp->old_buffer_repp != 0) {
      ::free(impl_repp->old_buffer_repp);
      priv_alloc_sample_buf(&impl_repp->old_buffer_repp, sizeof(sample_t) * reserved_samples_rep);
    }
  }

  if (len > buffersize_rep) {
    for(size_t n = 0; n < buffer.size(); n++) {
      /* note: mute starting from 'buffersize_rep' */
      for(buf_size_t m = buffersize_rep; m < reserved_samples_rep; m++) {
	buffer[n][m] = SAMPLE_SPECS::silent_value;
      }
    }
  }

  buffersize_rep = len;
}

/**
 * Prepares sample buffer object for resampling 
 * operations with params 'from_srate' and 
 * 'to_srate'. This functions is meant for 
 * doing memory allocations and other similar 
 * operations which cannot be performed 
 * with realtime guarantees.
 */
void SAMPLE_BUFFER::resample_init_memory(SAMPLE_SPECS::sample_rate_t from_srate,
					 SAMPLE_SPECS::sample_rate_t to_srate)
{
#ifdef ECA_COMPILE_SAMPLERATE
  ECA_LOG_MSG(ECA_LOGGER::system_objects, 
		"Resampler selected: libsamplerate (Secret Rabbit Code).");
#else
  ECA_LOG_MSG(ECA_LOGGER::system_objects, 
		"Resampler selected: internal resampler.");
#endif

  double step = 1.0;
  if (from_srate != 0) { step = static_cast<double>(to_srate) / from_srate; }

  /* add at least one word of extra space */
  buf_size_t new_buffer_size = static_cast<buf_size_t>((step * buffersize_rep)) + sizeof(buf_size_t);

  if (new_buffer_size > reserved_samples_rep) {
    reserved_samples_rep = new_buffer_size * 2;

#ifdef ECA_DEBUG_MODE
    DBC_CHECK(impl_repp->rt_lock_rep != true);
    DBC_CHECK(impl_repp->lockref_rep == 0);
#endif

    for(int c = 0; c < channel_count_rep; c++) {
      ::free(buffer[c]);
      priv_alloc_sample_buf(&buffer[c], sizeof(sample_t) * reserved_samples_rep);
    }
  }

#ifdef ECA_COMPILE_SAMPLERATE
  impl_repp->src_state_rep.resize(channel_count_rep);
  for(int c = 0; c < channel_count_rep; c++) {
    if (impl_repp->src_state_rep[c] == 0) {
      int error;
      impl_repp->src_state_rep[c] = src_new((impl_repp->quality_rep > 75) ? SRC_SINC_BEST_QUALITY : SRC_SINC_MEDIUM_QUALITY, 1, &error);
      DBC_CHECK(impl_repp->src_state_rep[c] != 0);
    }
  }
#endif

  if (impl_repp->old_buffer_repp == 0) {
#ifdef ECA_DEBUG_MODE
    DBC_CHECK(impl_repp->rt_lock_rep != true);
#endif
    priv_alloc_sample_buf(&impl_repp->old_buffer_repp, sizeof(sample_t) * reserved_samples_rep);
  }

  if (impl_repp->resample_memory_rep.size() < static_cast<size_t>(channel_count_rep)) {
#ifdef ECA_DEBUG_MODE
    DBC_CHECK(impl_repp->rt_lock_rep != true);
#endif
    impl_repp->resample_memory_rep.resize(channel_count_rep, 0.0f);
  }
}

void SAMPLE_BUFFER::reserve_channels(channel_size_t num)
{
  channel_size_t oldcount = number_of_channels();
  number_of_channels(num);
  number_of_channels(oldcount);
}

void SAMPLE_BUFFER::reserve_length_in_samples(buf_size_t len)
{
  buf_size_t oldlen = length_in_samples();
  length_in_samples(len);
  length_in_samples(oldlen);
}

/**
 * Sets the realtime-lock state. When realtime-lock
 * is enabled, all non-rt-safe operations 
 * like for instance memory allocations are
 * blocked.
 * 
 * @param state true=lock, false=unlock
 */
void SAMPLE_BUFFER::set_rt_lock(bool state)
{
  impl_repp->rt_lock_rep = state;
}

/** 
 * Increases reference count of 'buffer' data
 * area.
 *
 * This should be issued when an object uses
 * direct access to the samplebuffer's 
 * audio data buffer.
 * 
 * Note! release_pointer_reflock() must be 
 * called after caller stops accessing
 * 'buffer'.
 */
void SAMPLE_BUFFER::get_pointer_reflock(void)
{
  impl_repp->lockref_rep++;
}

/** 
 * Increases reference count of 'buffer' data
 * area.
 *
 * @see get_pointer_reflock()
 */
void SAMPLE_BUFFER::release_pointer_reflock(void)
{
  impl_repp->lockref_rep--;
  DBC_ENSURE(impl_repp->lockref_rep >= 0);
}

/**
 * Adds all event tags that are set for 'sbuf' (bitwise-OR).
 */
void SAMPLE_BUFFER::event_tags_add(const SAMPLE_BUFFER& sbuf)
{
  impl_repp->event_tags_rep |= 
    sbuf.impl_repp->event_tags_rep;
}

/**
 * Sets only those event flags that are set for 'sbuf'.
 */
void SAMPLE_BUFFER::event_tags_set(const SAMPLE_BUFFER& sbuf)
{
  impl_repp->event_tags_rep = 
    sbuf.impl_repp->event_tags_rep;
}

/**
 * Clears all tags matching 'tagmask'
 */
void SAMPLE_BUFFER::event_tags_clear(Tag_name tagmask)
{
  event_tag_set(tagmask, false);
}

/** 
 * Set/clears the event tag 'tag'.
 */
void SAMPLE_BUFFER::event_tag_set(Tag_name tag, bool val)
{
  if (val)
    impl_repp->event_tags_rep |= tag;
  else
    impl_repp->event_tags_rep &= ~tag;
}

bool SAMPLE_BUFFER::event_tag_test(Tag_name tag)
{
  return (impl_repp->event_tags_rep & tag) ? true : false;
}

/**
 * Resamples samplebuffer contents.
 *
 * Note! 'resample_init_memory()' must be called before 
 *       before calling this function.
 */
void SAMPLE_BUFFER::resample_nofilter(SAMPLE_SPECS::sample_rate_t from, 
				      SAMPLE_SPECS::sample_rate_t to)
{
  double step = static_cast<double>(to) / from;
  buf_size_t old_buffer_size = buffersize_rep;

  // truncate, not round, to integer
  length_in_samples(static_cast<buf_size_t>(std::floor(step * buffersize_rep)));

  DEBUG_RESAMPLING_STATEMENT(std::cerr << "resample_no_f from " << from << " to " << to << "." << std::endl);

  DBC_CHECK(impl_repp->old_buffer_repp != 0);

  for(int c = 0; c < channel_count_rep; c++) {
    std::memcpy(impl_repp->old_buffer_repp, buffer[c], old_buffer_size * sizeof(sample_t));

    DBC_CHECK(buffersize_rep <= reserved_samples_rep);
    
    double counter = 0.0;
    buf_size_t new_buffer_index = 0;
    buf_size_t interpolate_index = 0;
     
    buffer[c][0] = impl_repp->old_buffer_repp[0];
    for(buf_size_t old_buffer_index = 1; old_buffer_index < old_buffer_size; old_buffer_index++) {
      counter += step;
      if (step <= 1) {
	if (counter >= new_buffer_index + 1) {
	  new_buffer_index++;
	  if (new_buffer_index >= buffersize_rep) break;
	  buffer[c][new_buffer_index] = impl_repp->old_buffer_repp[old_buffer_index];
	}
      }
      else {
	new_buffer_index = static_cast<buf_size_t>(std::ceil(counter));
	if (new_buffer_index >= buffersize_rep) new_buffer_index = buffersize_rep - 1;
	for(buf_size_t t = interpolate_index + 1; t < new_buffer_index; t++) {
	  buffer[c][t] = impl_repp->old_buffer_repp[old_buffer_index - 1] + ((impl_repp->old_buffer_repp[old_buffer_index]
									      - impl_repp->old_buffer_repp[old_buffer_index-1])
									     * static_cast<SAMPLE_BUFFER::sample_t>(t - interpolate_index)
									     / (new_buffer_index - interpolate_index));
	}
	buffer[c][new_buffer_index] = impl_repp->old_buffer_repp[old_buffer_index];
      }
      interpolate_index = new_buffer_index;
    }
  }
}

/**
 * Resamples samplebuffer contents.
 *
 * Note! 'resample_init_memory()' must be called before 
 *       before calling this function.
 */
void SAMPLE_BUFFER::resample_with_memory(SAMPLE_SPECS::sample_rate_t from, 
					 SAMPLE_SPECS::sample_rate_t to)
{
  double step = (double)to / from;
  buf_size_t old_buffer_size = buffersize_rep;

  // truncate, not round, to integer
  length_in_samples(static_cast<buf_size_t>(std::floor(step * buffersize_rep)));

  DBC_CHECK(impl_repp->old_buffer_repp != 0);
  DEBUG_RESAMPLING_STATEMENT(std::cerr << "(samplebuffer) resample_w_m from " << from << " to " << to << "." << std::endl); 

  if (impl_repp->resample_memory_rep.size() < static_cast<size_t>(channel_count_rep)) {
    DBC_CHECK(impl_repp->rt_lock_rep != true);
    impl_repp->resample_memory_rep.resize(channel_count_rep, 0.0f);
  }

  length_in_samples(buffersize_rep);

  for(int c = 0; c < channel_count_rep; c++) {
    std::memcpy(impl_repp->old_buffer_repp, buffer[c], old_buffer_size * sizeof(sample_t));

    DBC_CHECK(buffersize_rep <= reserved_samples_rep);
    
    double counter = 0.0;
    buf_size_t new_buffer_index = 0;
    buf_size_t interpolate_index = -1;
    sample_t from_point;

    for(buf_size_t old_buffer_index = 0; old_buffer_index < old_buffer_size; old_buffer_index++) {
      counter += step;
      if (step <= 1) {
	if (counter >= new_buffer_index + 1) {
	  new_buffer_index++;
	  if (new_buffer_index >= buffersize_rep) break;
	  buffer[c][new_buffer_index] = impl_repp->old_buffer_repp[old_buffer_index];
	}
      }
      else {
	new_buffer_index = static_cast<buf_size_t>(std::ceil(counter));
	if (old_buffer_index == 0) from_point = impl_repp->resample_memory_rep[c];
	else from_point = impl_repp->old_buffer_repp[old_buffer_index-1];
	if (new_buffer_index >= buffersize_rep) new_buffer_index = buffersize_rep - 1;
	for(buf_size_t t = interpolate_index + 1; t < new_buffer_index; t++) {
	  buffer[c][t] = from_point + ((impl_repp->old_buffer_repp[old_buffer_index]
					- from_point)
				       * static_cast<SAMPLE_BUFFER::sample_t>(t - interpolate_index)
				       / (new_buffer_index - interpolate_index));
	}
	buffer[c][new_buffer_index] = impl_repp->old_buffer_repp[old_buffer_index];
      }
      interpolate_index = new_buffer_index;
    }
    impl_repp->resample_memory_rep[c] = impl_repp->old_buffer_repp[old_buffer_size - 1];
  }
}

void SAMPLE_BUFFER::resample_secret_rabbit_code(SAMPLE_SPECS::sample_rate_t from_srate,
						SAMPLE_SPECS::sample_rate_t to_srate) 
{
#ifdef ECA_COMPILE_SAMPLERATE
  SRC_DATA params;
  long ch_out_count = -1;
  double step = static_cast<double>(to_srate) / from_srate;
  buf_size_t old_buffer_size = buffersize_rep;

  /* modify buffersize_rep (size of dst buffer) */
  length_in_samples(static_cast<buf_size_t>(std::floor(step * buffersize_rep)));

  DBC_CHECK(impl_repp->old_buffer_repp != 0);
  DEBUG_RESAMPLING_STATEMENT(std::cerr << "(samplebuffer) resample_s_r_c from " << from_srate << " to " << to_srate << "." << std::endl); 

  for(int c = 0; c < channel_count_rep; c++) {
    std::memcpy(impl_repp->old_buffer_repp, buffer[c], old_buffer_size * sizeof(sample_t));

    DBC_CHECK(buffersize_rep <= reserved_samples_rep);

    // A pointer to the input data samples.
    params.data_in = impl_repp->old_buffer_repp; 

    // The number of frames of data pointed to by data_in.
    params.input_frames = old_buffer_size;

    // A pointer to the output data samples.
    params.data_out = buffer[c];

    // Maximum number of frames pointer to by data_out.
    // note: was 'reserved_samples_rep' but this led 
    //       to corrupted output
    params.output_frames = reserved_samples_rep; /* buffersize_rep; */

    // Equal to output_sample_rate / input_sample_rate.
    params.src_ratio = step;
    params.end_of_input = 0;

    // update the step
    int ret = src_set_ratio(impl_repp->src_state_rep[c], step);
    DBC_CHECK(ret == 0);

    // Perform the sample rate conversion
    ret = src_process(impl_repp->src_state_rep[c], &params);
    DBC_CHECK(ret == 0);
    if (ret) {
      /* make sure we avoid segfault in all cases */
      params.output_frames_gen = 0;
      params.input_frames_used = old_buffer_size;
    }
    DBC_CHECK(::labs(params.input_frames_used - old_buffer_size) == 0);
#ifdef ECA_DEBUG_MODE
    /* make sure all input samples have been used */
    if (old_buffer_size != params.input_frames_used) { std::cerr << "input_frames_over=" << old_buffer_size - params.input_frames_used << ".\n"; }

    /* check that all channels are processed in the same way */
    if (c == 0) ch_out_count = params.output_frames_gen;
    DBC_CHECK(ch_out_count == params.output_frames_gen);
#endif /* ECA_DEBUG_MODE */
  }

  DEBUG_RESAMPLING_STATEMENT(std::cerr << "(samplebuffer) src_src input=" << old_buffer_size 
			     << ", target=" << buffersize_rep 
			     << ", processed=" << params.input_frames_used 
			     << ", space_for=" << reserved_samples_rep 
			     << ", out=" << params.output_frames_gen << std::endl); 

#ifdef ECA_DEBUG_MODE
  if ((params.output_frames_gen - buffersize_rep) > 1) {
    std::cerr << "(samplebuffer) src_src input=" << old_buffer_size 
	      << ", target=" << buffersize_rep 
	      << ", processed=" << params.input_frames_used 
	      << ", space_for=" << reserved_samples_rep 
	      << ", out=" << params.output_frames_gen << std::endl; 
  }
#endif
  if (params.output_frames_gen != buffersize_rep) {
    /* note: we set buffersize_rep directly and bypass
     * length_in_samples(), but in this case it is safe as 
     * we have used 'reserved_samples_rep' as the upper limit */
    buffersize_rep = params.output_frames_gen;
  }
#endif /* ECA_COMPILE_SAMPLERATE */
}

void SAMPLE_BUFFER::resample_extfilter(SAMPLE_SPECS::sample_rate_t from_srate,
					SAMPLE_SPECS::sample_rate_t to_srate) 
{
}

void SAMPLE_BUFFER::resample_simplefilter(SAMPLE_SPECS::sample_rate_t from_srate,
					  SAMPLE_SPECS::sample_rate_t to_srate) 
{ 
}
