1436 lines
40 KiB
C++
1436 lines
40 KiB
C++
// ------------------------------------------------------------------------
|
|
// 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)++];
|
|
}
|
|
obuffer[optr] = eca_sample_convert_s16_to_float(*(int16_t*)a);
|
|
}
|
|
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)++];
|
|
}
|
|
obuffer[optr] = eca_sample_convert_s16_to_float(*(int16_t*)a);
|
|
}
|
|
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)++];
|
|
}
|
|
obuffer[optr] = eca_sample_convert_s32_to_float((*(int32_t*)b));
|
|
}
|
|
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 */
|
|
}
|
|
obuffer[optr] = eca_sample_convert_s32_to_float((*(int32_t*)b));
|
|
}
|
|
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)++];
|
|
}
|
|
obuffer[optr] = eca_sample_convert_s32_to_float(*(int32_t*)b);
|
|
}
|
|
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)++];
|
|
}
|
|
obuffer[optr] = eca_sample_convert_s32_to_float(*(int32_t*)b);
|
|
}
|
|
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)++];
|
|
}
|
|
obuffer[optr] = (sample_t)(*(float*)b);
|
|
}
|
|
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)++];
|
|
}
|
|
obuffer[optr] = (sample_t)(*(float*)b);
|
|
}
|
|
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], ¶ms);
|
|
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)
|
|
{
|
|
}
|