|
List Info
Thread: Datahandle State API changes
|
|
| Datahandle State API changes |

|
2006-11-06 12:39:16 |
Hi!
I've now implemented an API and implementation in
datahandles to query
for the length of the resampler filter state (as
brainstormed on IRC),
which should also be usable for other cases (such as an FIR
lowpass
handle). Here are the changes - including documentation - so
please let
me know if there is anything you'd like to see fixed.
As for the elegance of the test (which reads single
samples), it could
be made more elegant by using a loop handle, but I think
this can be
done as a seperate commit. I am not sure it is worth it,
though. Its
just a test after all.
There is one issue I was unsure with, and that is the actual
meaning of
the gsl_data_handle_get_source() function. I had assumed
that this
function would return the datahandle which provided the
data, and thus,
for the resampling case, it should return the unresampled
datahandle.
So I added a get_source() method to the resampling
datahandle. However,
some other datahandles where I find it obvious to implement
get_source
in a similar way, for instance the reversed handle, do not
have any
get_source function. Especially, there is no
chain_handle_get_source()
function in gsldatahandle.c, and any handle derived from the
chained
handle doesn't have a get_source function.
Additionally, there is no documentation for
gsl_data_handle_get_source(), so there is no way to look up
whats the
intended meaning. So maybe I was wrong about implementing it
for the
resampling handle, or maybe it should be implemented for
more handles.
Cu... Stefan
Index: bse/gsldatahandle-vorbis.c
============================================================
=======
--- bse/gsldatahandle-vorbis.c (revision 4068)
+++ bse/gsldatahandle-vorbis.c (working copy)
 -365,6
+365,7  static GslDataHandleFuncs dh_vorbis_vtab
dh_vorbis_read,
dh_vorbis_close,
NULL,
+ NULL,
dh_vorbis_destroy,
};
Index: bse/gsldatahandle-mad.c
============================================================
=======
--- bse/gsldatahandle-mad.c (revision 4068)
+++ bse/gsldatahandle-mad.c (working copy)
 -673,6
+673,7  static GslDataHandleFuncs dh_mad_vtable
dh_mad_read,
dh_mad_close,
NULL,
+ NULL,
dh_mad_destroy,
};
Index: bse/ChangeLog
============================================================
=======
--- bse/ChangeLog (revision 4068)
+++ bse/ChangeLog (working copy)
 -1,3
+1,19 
+Mon Nov 6 13:12:50 2006 Stefan Westerfeld <stefan space.twc.de>
+
+ * gsldatahandle.[hc]: Added new
gsl_data_handle_get_state_length
+ function for datahandles, with a corresponding vtable
entry. For
+ filtering datahandles (such as lowpass handles), which are
usually
+ stateful, it returns the filter state length.
+
+ * gsldatahandle-vorbis.c:
+ * gsldatahandle-mad.c:
+ * bsedatahandle-resample.cc:
+ * tests/loophandle.c: Implement the get_state_length
datahandle
+ method.
+
+ * tests/resamplehandle.cc: Test the resampler
get_state_length
+ function.
+
Sun Nov 5 04:23:07 2006 Tim Janik <timj gtk.org>
* tests/filtertest.cc (random_filter_tests): extended
fixed orders to 32
Index: bse/gsldatahandle.c
============================================================
=======
--- bse/gsldatahandle.c (revision 4068)
+++ bse/gsldatahandle.c (working copy)
 -195,6
+195,41  gsl_data_handle_get_source (GslDataHandl
return src_handle;
}
+/**
+ * param data_handle a DataHandle
+ * return the state length of the data handle
+ *
+ * Some data handles produce output samples from an input
data handle,
+ * like filtering and resampling datahandles. Usually, they
have an internal
+ * state which means that the value of one input sample
affects not only one
+ * output sample, but some samples before and some samples
after the
+ * "corresponding" output sample.
+ *
+ * Often the state is symmetric, so that the number of
output samples affected
+ * before and after the "corresponding" output
sample is the same. Then the
+ * function returns this number. If the state is
asymmetric, this function
+ * shall return the maximum of the two numbers.
+ *
+ * If multiple data handles are cascaded (for instance when
resampling a
+ * filtered signal), the function propagates the state
length, so that the
+ * state of the chain of all operations together is
returned.
+ *
+ * Note: This function can only be used while the data
handle is opened.
+ *
+ * This function is MT-safe and may be called from any
thread.
+ */
+int64
+gsl_data_handle_get_state_length (GslDataHandle *dhandle)
+{
+ g_return_val_if_fail (dhandle != NULL, -1);
+ g_return_val_if_fail (dhandle->open_count > 0, -1);
+
+ GSL_SPIN_LOCK (&dhandle->mutex);
+ int64 state_length =
dhandle->vtable->get_state_length ?
dhandle->vtable->get_state_length (dhandle) : 0;
+ GSL_SPIN_UNLOCK (&dhandle->mutex);
+ return state_length;
+}
+
int64
gsl_data_handle_length (GslDataHandle *dhandle)
{
 -355,6
+390,7  gsl_data_handle_new_mem (guint n
mem_handle_read,
mem_handle_close,
NULL,
+ NULL,
mem_handle_destroy,
};
MemHandle *mhandle;
 -500,6
+536,14  xinfo_get_source_handle (GslDataHandle *
return chandle->src_handle;
}
+static int64
+xinfo_get_state_length (GslDataHandle *dhandle)
+{
+ XInfoHandle *chandle = (XInfoHandle*) dhandle;
+ return gsl_data_handle_get_state_length
(chandle->src_handle);
+}
+
+
static GslDataHandle*
xinfo_data_handle_new (GslDataHandle *src_handle,
gboolean clear_xinfos,
 -511,6
+555,7  xinfo_data_handle_new (GslDataHandle *sr
xinfo_handle_read,
xinfo_handle_close,
xinfo_get_source_handle,
+ xinfo_get_state_length,
xinfo_handle_destroy,
};
SfiRing *dest_added = NULL, *dest_remove = NULL;
 -686,6
+731,12  chain_handle_close (GslDataHandle *dhand
gsl_data_handle_close (chandle->src_handle);
}
+static int64
+chain_handle_get_state_length (GslDataHandle *dhandle)
+{
+ ChainHandle *chandle = (ChainHandle*) dhandle;
+ return gsl_data_handle_get_state_length
(chandle->src_handle);
+}
/* --- reversed handle --- */
static void
 -745,6
+796,7  gsl_data_handle_new_reverse (GslDataHand
reverse_handle_read,
chain_handle_close,
NULL,
+ chain_handle_get_state_length,
reverse_handle_destroy,
};
ReversedHandle *rhandle;
 -853,6
+905,7  gsl_data_handle_new_translate (GslDataHa
cut_handle_read,
chain_handle_close,
NULL,
+ chain_handle_get_state_length,
cut_handle_destroy,
};
CutHandle *chandle;
 -1032,6
+1085,14  insert_handle_read (GslDataHandle *dhand
return orig_n_values - n_values;
}
+static int64
+insert_handle_get_state_length (GslDataHandle *dhandle)
+{
+ InsertHandle *ihandle = (InsertHandle*) dhandle;
+ return gsl_data_handle_get_state_length
(ihandle->src_handle);
+}
+
+
GslDataHandle*
gsl_data_handle_new_insert (GslDataHandle *src_handle,
guint paste_bit_depth,
 -1045,6
+1106,7  gsl_data_handle_new_insert (GslDataHandl
insert_handle_read,
insert_handle_close,
NULL,
+ insert_handle_get_state_length,
insert_handle_destroy,
};
InsertHandle *ihandle;
 -1161,6
+1223,7  gsl_data_handle_new_looped (GslDataHandl
loop_handle_read,
chain_handle_close,
NULL,
+ chain_handle_get_state_length,
loop_handle_destroy,
};
LoopHandle *lhandle;
 -1259,6
+1322,13  dcache_handle_get_source_handle (GslData
return chandle->dcache->dhandle;
}
+static int64
+dcache_handle_get_state_length (GslDataHandle *dhandle)
+{
+ DCacheHandle *chandle = (DCacheHandle*) dhandle;
+ return gsl_data_handle_get_state_length
(chandle->dcache->dhandle);
+}
+
GslDataHandle*
gsl_data_handle_new_dcached (GslDataCache *dcache)
{
 -1267,6
+1337,7  gsl_data_handle_new_dcached (GslDataCach
dcache_handle_read,
dcache_handle_close,
dcache_handle_get_source_handle,
+ dcache_handle_get_state_length,
dcache_handle_destroy,
};
DCacheHandle *dhandle;
 -1495,6
+1566,7  gsl_wave_handle_new (const gchar *f
wave_handle_read,
wave_handle_close,
NULL,
+ NULL,
wave_handle_destroy,
};
WaveHandle *whandle;
Index: bse/gsldatahandle.h
============================================================
=======
--- bse/gsldatahandle.h (revision 4068)
+++ bse/gsldatahandle.h (working copy)
 -63,6
+63,7  struct _GslDataHandleFuncs
gfloat *values);
void (*close) (GslDataHandle *data_handle);
GslDataHandle* (*get_source) (GslDataHandle
*data_handle);
+ int64 (*get_state_length) (GslDataHandle
*data_handle);
void (*destroy) (GslDataHandle *data_handle);
};
 -85,6
+86,7  int64 gsl_data_handle_read (GslDataH
int64 value_offset,
int64 n_values,
gfloat *values);
+int64 gsl_data_handle_get_state_length (GslDataHandle
*dhandle);
GslDataHandle* gsl_data_handle_get_source
(GslDataHandle *dhandle);
GslDataHandle* gsl_data_handle_new_cut (GslDataHandle
*src_handle,
int64 cut_offset,
Index: bse/tests/loophandle.c
============================================================
=======
--- bse/tests/loophandle.c (revision 4068)
+++ bse/tests/loophandle.c (working copy)
 -105,6
+105,14  loop_handle_reference_read (GslDataHandl
}
}
+static int64
+loop_handle_reference_get_state_length (GslDataHandle
*dhandle)
+{
+ LoopHandleReference *lhandle = (LoopHandleReference*)
dhandle;
+ return gsl_data_handle_get_state_length
(lhandle->src_handle);
+}
+
+
static GslDataHandle*
gsl_data_handle_new_looped_reference (GslDataHandle
*src_handle,
GslLong loop_first,
 -115,6
+123,7  gsl_data_handle_new_looped_reference (Gs
loop_handle_reference_read,
loop_handle_reference_close,
NULL,
+ loop_handle_reference_get_state_length,
loop_handle_reference_destroy,
};
LoopHandleReference *lhandle;
Index: bse/tests/resamplehandle.cc
============================================================
=======
--- bse/tests/resamplehandle.cc (revision 4068)
+++ bse/tests/resamplehandle.cc (working copy)
 -371,6
+371,111  test_delay_compensation (const char *run
TDONE();
}
+static void
+test_state_length (const char *run_type)
+{
+ TSTART ("Resampler State Length Info (%s)",
run_type);
+
+
//----------------------------------------------------------
-------------------------
+ // usampling
+
//----------------------------------------------------------
-------------------------
+ {
+ const guint period_size = 107;
+
+ /* fill input with 2 periods of a sine wave, so that
while at the start and
+ * at the end clicks occur (because the unwindowed
signal is assumed to 0 by
+ * the resamplehandle), in the middle 1 period can be
found that is clickless
+ */
+ vector<float> input (period_size * 2);
+ for (size_t i = 0; i < input.size(); i++)
+ input[i] = sin (i * 2 * M_PI / period_size);
+
+ const guint precision_bits = 16;
+ GslDataHandle *ihandle = gsl_data_handle_new_mem (1,
32, 44100, 440, input.size(), &input[0], NULL);
+ GslDataHandle *rhandle = bse_data_handle_new_upsample2
(ihandle, precision_bits);
+ BseErrorType open_error = gsl_data_handle_open
(rhandle);
+ TASSERT (open_error == 0);
+ TASSERT (gsl_data_handle_get_state_length (ihandle) ==
0);
+
+ // determine how much of the end of the signal is
"unusable" due to the resampler state:
+ const int64 state_length =
gsl_data_handle_get_state_length (rhandle);
+
+ /* read resampled signal in the range unaffected by the
resampler state (that
+ * is: not at the directly at the beginning, and not
directly at the end)
+ */
+ vector<float> output (input.size() * 3);
+ for (size_t values_done = 0; values_done <
output.size(); values_done++)
+ {
+ /* NOTE: this is an inlined implementation of a loop,
which you normally would
+ * implement with a loop handle, and it is inefficient
because we read the
+ * samples one-by-one -> usually: don't use such code,
always read in blocks */
+ int64 read_pos = (values_done + state_length) %
(period_size * 2) + (period_size * 2 - state_length);
+ TCHECK (read_pos >= state_length); /* check that
input signal was long enough to be for this test */
+ int64 values_read = gsl_data_handle_read (rhandle,
read_pos, 1, &output[values_done]);
+ TCHECK (values_read == 1);
+ }
+ double error = 0;
+ for (size_t i = 0; i < output.size(); i++)
+ {
+ double expected = sin (i * 2 * M_PI / (period_size * 2));
+ error = MAX (error, fabs (output[i] - expected));
+ }
+ double error_db = bse_db_from_factor (error, -200);
+ TASSERT (error_db < -97);
+ }
+
+
//----------------------------------------------------------
-------------------------
+ // downsampling
+
//----------------------------------------------------------
-------------------------
+
+ {
+ const guint period_size = 190;
+
+ /* fill input with 2 periods of a sine wave, so that
while at the start and
+ * at the end clicks occur (because the unwindowed
signal is assumed to 0 by
+ * the resamplehandle), in the middle 1 period can be
found that is clickless
+ */
+ vector<float> input (period_size * 2);
+ for (size_t i = 0; i < input.size(); i++)
+ input[i] = sin (i * 2 * M_PI / period_size);
+
+ const guint precision_bits = 16;
+ GslDataHandle *ihandle = gsl_data_handle_new_mem (1,
32, 44100, 440, input.size(), &input[0], NULL);
+ GslDataHandle *rhandle =
bse_data_handle_new_downsample2 (ihandle, precision_bits);
+ BseErrorType open_error = gsl_data_handle_open
(rhandle);
+ TASSERT (open_error == 0);
+ TASSERT (gsl_data_handle_get_state_length (ihandle) ==
0);
+
+ // determine how much of the end of the signal is
"unusable" due to the resampler state:
+ const int64 state_length =
gsl_data_handle_get_state_length (rhandle);
+
+ /* read resampled signal in the range unaffected by the
resampler state (that
+ * is: not at the directly at the beginning, and not
directly at the end)
+ */
+ vector<float> output (input.size() * 3 / 2);
+ for (size_t values_done = 0; values_done <
output.size(); values_done++)
+ {
+ /* NOTE: this is an inlined implementation of a loop,
which you normally would
+ * implement with a loop handle, and it is inefficient
because we read the
+ * samples one-by-one -> usually: don't use such code,
always read in blocks */
+ int64 read_pos = (values_done + state_length) %
(period_size / 2) + (period_size / 2 - state_length);
+ TCHECK (read_pos >= state_length); /* check that
input signal was long enough to be for this test */
+ int64 values_read = gsl_data_handle_read (rhandle,
read_pos, 1, &output[values_done]);
+ TCHECK (values_read == 1);
+ }
+ double error = 0;
+ for (size_t i = 0; i < output.size(); i++)
+ {
+ double expected = sin (i * 2 * M_PI / (period_size / 2));
+ error = MAX (error, fabs (output[i] - expected));
+ }
+ double error_db = bse_db_from_factor (error, -200);
+ TASSERT (error_db < -105);
+ }
+ TDONE();
+}
+
+
int
main (int argc,
char *argv[])
 -385,6
+490,7  main (int argc,
test_c_api ("FPU");
test_delay_compensation ("FPU");
+ test_state_length ("FPU");
run_tests ("FPU");
/* load plugins */
 -399,6
+505,7  main (int argc,
test_c_api ("SSE");
test_delay_compensation ("SSE");
+ test_state_length ("SSE");
run_tests ("SSE");
return 0;
Index: bse/bsedatahandle-resample.cc
============================================================
=======
--- bse/bsedatahandle-resample.cc (revision 4068)
+++ bse/bsedatahandle-resample.cc (working copy)
 -136,7
+136,7  protected:
}
/* implemented by upsampling and downsampling datahandle
*/
- virtual BseResampler2Mode mode () = 0;
+ virtual BseResampler2Mode mode () const = 0;
virtual int64 read_frame (int64 frame) = 0;
public:
 -222,18
+222,38  public:
return n_values;
}
+ GslDataHandle*
+ get_source() const
+ {
+ return gsl_data_handle_get_source (m_src_handle);
+ }
+ int64
+ get_state_length() const
+ {
+ int64 source_state_length =
gsl_data_handle_get_state_length (m_src_handle);
+ if (source_state_length < 0)
+ return source_state_length;
+ if (mode() == BSE_RESAMPLER2_MODE_UPSAMPLE)
+ source_state_length *= 2;
+ else
+ source_state_length = (source_state_length + 1) / 2;
+
+ if (m_resamplers.size())
+ {
+ /* For fractional delays, a delay of 10.5 for instance
means that input[0]
+ * affects samples 10 and 11 are affected, and thus the
state length we
+ * assume for that case is 11.
+ */
+ int64 per_channel_state = ceil
(m_resamplers[0]->delay());
+ return source_state_length + per_channel_state *
m_dhandle.setup.n_channels;
+ }
+ // should never happen
+ return -1;
+ }
static GslDataHandle*
dh_create (DataHandleResample2 *cxx_dh)
{
- static GslDataHandleFuncs dh_vtable =
- {
- dh_open,
- dh_read,
- dh_close,
- NULL,
- dh_destroy,
- };
if (cxx_dh->m_init_ok)
{
cxx_dh->m_dhandle.vtable = &dh_vtable;
 -246,9
+266,10  public:
return NULL;
}
}
-
private:
/* for the "C" API (vtable) */
+ static GslDataHandleFuncs dh_vtable;
+
static DataHandleResample2*
dh_cast (GslDataHandle *dhandle)
{
 -278,6
+299,26  private:
{
return dh_cast (dhandle)->read (voffset, n_values,
values);
}
+ static GslDataHandle*
+ dh_get_source (GslDataHandle *dhandle)
+ {
+ return dh_cast (dhandle)->get_source();
+ }
+ static int64
+ dh_get_state_length (GslDataHandle *dhandle)
+ {
+ return dh_cast (dhandle)->get_state_length();
+ }
+};
+
+GslDataHandleFuncs DataHandleResample2::dh_vtable =
+{
+ dh_open,
+ dh_read,
+ dh_close,
+ dh_get_source,
+ dh_get_state_length,
+ dh_destroy,
};
class DataHandleUpsample2 : public DataHandleResample2
 -291,7
+332,7  public:
m_dhandle.name = g_strconcat (m_src_handle->name,
"// #upsample2 /", NULL);
}
BseResampler2Mode
- mode()
+ mode() const
{
return BSE_RESAMPLER2_MODE_UPSAMPLE;
}
 -366,7
+407,7  public:
{
}
BseResampler2Mode
- mode()
+ mode() const
{
return BSE_RESAMPLER2_MODE_DOWNSAMPLE;
}
--
Stefan Westerfeld, Hamburg/Germany, http://space.twc.de/~stef
an
_______________________________________________
beast mailing list
beast gnome.org
http://m
ail.gnome.org/mailman/listinfo/beast
|
|
| Datahandle State API changes |

|
2006-11-25 16:48:51 |
On Mon, 6 Nov 2006, Stefan Westerfeld wrote:
> There is one issue I was unsure with, and that is the
actual meaning of
> the gsl_data_handle_get_source() function. I had
assumed that this
> function would return the datahandle which provided the
data, and thus,
> for the resampling case, it should return the
unresampled datahandle.
>
> So I added a get_source() method to the resampling
datahandle. However,
> some other datahandles where I find it obvious to
implement get_source
> in a similar way, for instance the reversed handle, do
not have any
> get_source function. Especially, there is no
chain_handle_get_source()
> function in gsldatahandle.c, and any handle derived
from the chained
> handle doesn't have a get_source function.
>
> Additionally, there is no documentation for
> gsl_data_handle_get_source(), so there is no way to
look up whats the
> intended meaning. So maybe I was wrong about
implementing it for the
> resampling handle, or maybe it should be implemented
for more handles.
it usually is a good idea to look at the actual use cases to
find out
about an unknown function. in this case:
bse_storage_put_data_handle()
do /* skip comment or cache handles */
{
test_handle = tmp_handle;
tmp_handle = gsl_data_handle_get_source
(test_handle);
}
while (tmp_handle); /* skip comment or cache handles
*/
so:
no, resmaplers should not implement get_source, that
function is only intended
for unrolling the datahandle chain as long as the data
(length, number of
channels, accuracy) stays exactly the same.
>
> Cu... Stefan
>
> Index: bse/gsldatahandle-vorbis.c
>
============================================================
=======
> --- bse/gsldatahandle-vorbis.c (revision 4068)
> +++ bse/gsldatahandle-vorbis.c (working copy)
>  -365,6 +365,7  static GslDataHandleFuncs dh_vorbis_vtab
> dh_vorbis_read,
> dh_vorbis_close,
> NULL,
> + NULL,
> dh_vorbis_destroy,
> };
>
> Index: bse/gsldatahandle-mad.c
>
============================================================
=======
> --- bse/gsldatahandle-mad.c (revision 4068)
> +++ bse/gsldatahandle-mad.c (working copy)
>  -673,6 +673,7  static GslDataHandleFuncs dh_mad_vtable
> dh_mad_read,
> dh_mad_close,
> NULL,
> + NULL,
> dh_mad_destroy,
> };
>
> Index: bse/ChangeLog
>
============================================================
=======
> --- bse/ChangeLog (revision 4068)
> +++ bse/ChangeLog (working copy)
>  -1,3 +1,19 
> +Mon Nov 6 13:12:50 2006 Stefan Westerfeld
<stefan space.twc.de>
> +
> + * gsldatahandle.[hc]: Added new
gsl_data_handle_get_state_length
> + function for datahandles, with a corresponding vtable
entry. For
> + filtering datahandles (such as lowpass handles),
which are usually
> + stateful, it returns the filter state length.
> +
> + * gsldatahandle-vorbis.c:
> + * gsldatahandle-mad.c:
> + * bsedatahandle-resample.cc:
> + * tests/loophandle.c: Implement the get_state_length
datahandle
> + method.
> +
> + * tests/resamplehandle.cc: Test the resampler
get_state_length
> + function.
> +
> Sun Nov 5 04:23:07 2006 Tim Janik <timj gtk.org>
>
> * tests/filtertest.cc (random_filter_tests): extended
fixed orders to 32
> Index: bse/gsldatahandle.c
>
============================================================
=======
> --- bse/gsldatahandle.c (revision 4068)
> +++ bse/gsldatahandle.c (working copy)
>  -195,6 +195,41  gsl_data_handle_get_source (GslDataHandl
> return src_handle;
> }
>
> +/**
> + * param data_handle a DataHandle
> + * return the state length of the data handle
> + *
> + * Some data handles produce output samples from an
input data handle,
s/some/most/
> + * like filtering and resampling datahandles. Usually,
they have an internal
s/usually they/some/
> + * state which means that the value of one input
sample affects not only one
> + * output sample, but some samples before and some
samples after the
s/and/or/ (since that depends on the filter type)
> + * "corresponding" output sample.
> + *
> + * Often the state is symmetric, so that the number of
output samples affected
> + * before and after the "corresponding"
output sample is the same. Then the
> + * function returns this number. If the state is
asymmetric, this function
> + * shall return the maximum of the two numbers.
> + *
> + * If multiple data handles are cascaded (for instance
when resampling a
hm, "cascaded"? i'd rather say "nested"
(or "chained") here.
> + * filtered signal), the function propagates the state
length, so that the
> + * state of the chain of all operations together is
returned.
"state of the chain"? shouldn't that be
"accumulated state length" or so?
> + *
> + * Note: This function can only be used while the data
handle is opened.
> + *
> + * This function is MT-safe and may be called from any
thread.
> + */
> +int64
> +gsl_data_handle_get_state_length (GslDataHandle
*dhandle)
> +{
> + g_return_val_if_fail (dhandle != NULL, -1);
> + g_return_val_if_fail (dhandle->open_count > 0,
-1);
> +
> + GSL_SPIN_LOCK (&dhandle->mutex);
> + int64 state_length =
dhandle->vtable->get_state_length ?
dhandle->vtable->get_state_length (dhandle) : 0;
> + GSL_SPIN_UNLOCK (&dhandle->mutex);
> + return state_length;
> +}
> +
> int64
> gsl_data_handle_length (GslDataHandle *dhandle)
> {
>  -355,6 +390,7  gsl_data_handle_new_mem (guint n
> mem_handle_read,
> mem_handle_close,
> NULL,
> + NULL,
> mem_handle_destroy,
> };
> MemHandle *mhandle;
>  -500,6 +536,14  xinfo_get_source_handle (GslDataHandle *
> return chandle->src_handle;
> }
>
> +static int64
> +xinfo_get_state_length (GslDataHandle *dhandle)
> +{
> + XInfoHandle *chandle = (XInfoHandle*) dhandle;
> + return gsl_data_handle_get_state_length
(chandle->src_handle);
> +}
> +
> +
> static GslDataHandle*
> xinfo_data_handle_new (GslDataHandle *src_handle,
> gboolean clear_xinfos,
>  -511,6 +555,7  xinfo_data_handle_new (GslDataHandle *sr
> xinfo_handle_read,
> xinfo_handle_close,
> xinfo_get_source_handle,
> + xinfo_get_state_length,
> xinfo_handle_destroy,
> };
> SfiRing *dest_added = NULL, *dest_remove = NULL;
>  -686,6 +731,12  chain_handle_close (GslDataHandle *dhand
> gsl_data_handle_close (chandle->src_handle);
> }
>
> +static int64
> +chain_handle_get_state_length (GslDataHandle *dhandle)
> +{
> + ChainHandle *chandle = (ChainHandle*) dhandle;
> + return gsl_data_handle_get_state_length
(chandle->src_handle);
> +}
>
> /* --- reversed handle --- */
> static void
>  -745,6 +796,7  gsl_data_handle_new_reverse (GslDataHand
> reverse_handle_read,
> chain_handle_close,
> NULL,
> + chain_handle_get_state_length,
> reverse_handle_destroy,
> };
> ReversedHandle *rhandle;
>  -853,6 +905,7  gsl_data_handle_new_translate (GslDataHa
> cut_handle_read,
> chain_handle_close,
> NULL,
> + chain_handle_get_state_length,
> cut_handle_destroy,
> };
> CutHandle *chandle;
>  -1032,6 +1085,14  insert_handle_read
(GslDataHandle *dhand
> return orig_n_values - n_values;
> }
>
> +static int64
> +insert_handle_get_state_length (GslDataHandle
*dhandle)
> +{
> + InsertHandle *ihandle = (InsertHandle*) dhandle;
> + return gsl_data_handle_get_state_length
(ihandle->src_handle);
> +}
> +
> +
> GslDataHandle*
> gsl_data_handle_new_insert (GslDataHandle *src_handle,
> guint paste_bit_depth,
>  -1045,6 +1106,7  gsl_data_handle_new_insert
(GslDataHandl
> insert_handle_read,
> insert_handle_close,
> NULL,
> + insert_handle_get_state_length,
> insert_handle_destroy,
> };
> InsertHandle *ihandle;
>  -1161,6 +1223,7  gsl_data_handle_new_looped
(GslDataHandl
> loop_handle_read,
> chain_handle_close,
> NULL,
> + chain_handle_get_state_length,
> loop_handle_destroy,
> };
> LoopHandle *lhandle;
>  -1259,6 +1322,13 
dcache_handle_get_source_handle (GslData
> return chandle->dcache->dhandle;
> }
>
> +static int64
> +dcache_handle_get_state_length (GslDataHandle
*dhandle)
> +{
> + DCacheHandle *chandle = (DCacheHandle*) dhandle;
> + return gsl_data_handle_get_state_length
(chandle->dcache->dhandle);
> +}
> +
> GslDataHandle*
> gsl_data_handle_new_dcached (GslDataCache *dcache)
> {
>  -1267,6 +1337,7 
gsl_data_handle_new_dcached (GslDataCach
> dcache_handle_read,
> dcache_handle_close,
> dcache_handle_get_source_handle,
> + dcache_handle_get_state_length,
> dcache_handle_destroy,
> };
> DCacheHandle *dhandle;
>  -1495,6 +1566,7  gsl_wave_handle_new (const
gchar *f
> wave_handle_read,
> wave_handle_close,
> NULL,
> + NULL,
> wave_handle_destroy,
> };
> WaveHandle *whandle;
rest looks pretty good.
> Index: bse/bsedatahandle-resample.cc
>
============================================================
=======
> --- bse/bsedatahandle-resample.cc (revision 4068)
> +++ bse/bsedatahandle-resample.cc (working copy)
>  -136,7 +136,7  protected:
> }
>
> /* implemented by upsampling and downsampling
datahandle */
> - virtual BseResampler2Mode mode () = 0;
> + virtual BseResampler2Mode mode () const = 0;
> virtual int64 read_frame (int64 frame) = 0;
>
> public:
>  -222,18 +222,38  public:
>
> return n_values;
> }
> + GslDataHandle*
> + get_source() const
> + {
> + return gsl_data_handle_get_source (m_src_handle);
> + }
> + int64
> + get_state_length() const
> + {
> + int64 source_state_length =
gsl_data_handle_get_state_length (m_src_handle);
> + if (source_state_length < 0)
> + return source_state_length;
huh? why should a datahandle every return a negative state?
>
> + if (mode() == BSE_RESAMPLER2_MODE_UPSAMPLE)
> + source_state_length *= 2;
> + else
> + source_state_length = (source_state_length + 1)
/ 2;
> +
> + if (m_resamplers.size())
> + {
> + /* For fractional delays, a delay of 10.5 for
instance means that input[0]
> + * affects samples 10 and 11 are affected, and thus
the state length we
> + * assume for that case is 11.
> + */
the comment needs rewording, you use "affect" two
times.
> + int64 per_channel_state = ceil
(m_resamplers[0]->delay());
> + return source_state_length + per_channel_state *
m_dhandle.setup.n_channels;
> + }
> + // should never happen
> + return -1;
eeeek!
-1?
how is that *possibly* a valid state?
note that gsl_data_handle_get_state_length() was not
defined/discussed/documented as a function that can possibly
fail,
so why don't you return 0 here?
> + }
> static GslDataHandle*
> dh_create (DataHandleResample2 *cxx_dh)
> {
> - static GslDataHandleFuncs dh_vtable =
> - {
> - dh_open,
> - dh_read,
> - dh_close,
> - NULL,
> - dh_destroy,
> - };
huh?
> if (cxx_dh->m_init_ok)
> {
> cxx_dh->m_dhandle.vtable = &dh_vtable;
>  -246,9 +266,10  public:
> return NULL;
> }
> }
> -
> private:
> /* for the "C" API (vtable) */
> + static GslDataHandleFuncs dh_vtable;
> +
> static DataHandleResample2*
> dh_cast (GslDataHandle *dhandle)
> {
>  -278,6 +299,26  private:
> {
> return dh_cast (dhandle)->read (voffset,
n_values, values);
> }
> + static GslDataHandle*
> + dh_get_source (GslDataHandle *dhandle)
> + {
> + return dh_cast (dhandle)->get_source();
> + }
> + static int64
> + dh_get_state_length (GslDataHandle *dhandle)
> + {
> + return dh_cast (dhandle)->get_state_length();
> + }
> +};
> +
> +GslDataHandleFuncs DataHandleResample2::dh_vtable =
> +{
> + dh_open,
> + dh_read,
> + dh_close,
> + dh_get_source,
> + dh_get_state_length,
> + dh_destroy,
> };
erk. declaring the table inside the class is not neccessary
and defines an additional external symbol. decls should be
moved into the innermost scope possible, so please revert
that
part by moving the table back into dh_create.
>
> class DataHandleUpsample2 : public DataHandleResample2
>  -291,7 +332,7  public:
> m_dhandle.name = g_strconcat
(m_src_handle->name, "// #upsample2 /", NULL);
> }
> BseResampler2Mode
> - mode()
> + mode() const
> {
> return BSE_RESAMPLER2_MODE_UPSAMPLE;
> }
>  -366,7 +407,7  public:
> {
> }
> BseResampler2Mode
> - mode()
> + mode() const
> {
> return BSE_RESAMPLER2_MODE_DOWNSAMPLE;
> }
---
ciaoTJ
_______________________________________________
beast mailing list
beast gnome.org
http://m
ail.gnome.org/mailman/listinfo/beast
|
|
| Datahandle State API changes |

|
2006-11-27 17:15:50 |
Hi!
On Sat, Nov 25, 2006 at 05:48:51PM +0100, Tim Janik wrote:
> On Mon, 6 Nov 2006, Stefan Westerfeld wrote:
>
> >There is one issue I was unsure with, and that is
the actual meaning of
> >the gsl_data_handle_get_source() function. I had
assumed that this
> >function would return the datahandle which provided
the data, and thus,
> >for the resampling case, it should return the
unresampled datahandle.
> >
> >So I added a get_source() method to the resampling
datahandle. However,
> >some other datahandles where I find it obvious to
implement get_source
> >in a similar way, for instance the reversed handle,
do not have any
> >get_source function. Especially, there is no
chain_handle_get_source()
> >function in gsldatahandle.c, and any handle derived
from the chained
> >handle doesn't have a get_source function.
> >
> >Additionally, there is no documentation for
> >gsl_data_handle_get_source(), so there is no way to
look up whats the
> >intended meaning. So maybe I was wrong about
implementing it for the
> >resampling handle, or maybe it should be
implemented for more handles.
>
> it usually is a good idea to look at the actual use
cases to find out
> about an unknown function. in this case:
> bse_storage_put_data_handle()
> do /* skip comment or cache handles */
> {
> test_handle = tmp_handle;
> tmp_handle = gsl_data_handle_get_source
(test_handle);
> }
> while (tmp_handle); /* skip comment or cache
handles */
I see.
> so:
> no, resmaplers should not implement get_source, that
function is only
> intended
> for unrolling the datahandle chain as long as the data
(length, number of
> channels, accuracy) stays exactly the same.
Ok, I removed it again from the resampling handles.
> [...]
>
> >Index: bse/gsldatahandle.c
>
>========================================================
===========
> >--- bse/gsldatahandle.c (revision 4068)
> >+++ bse/gsldatahandle.c (working copy)
> > -195,6 +195,41  gsl_data_handle_get_source (GslDataHandl
> > return src_handle;
> >}
> >
> >+/**
> >+ * param data_handle a DataHandle
> >+ * return the state length of the data
handle
> >+ *
> >+ * Some data handles produce output samples from
an input data handle,
>
> s/some/most/
>
> >+ * like filtering and resampling datahandles.
Usually, they have an
> >internal
>
> s/usually they/some/
Ok, I made these changes, but since the examples I give
(resample/filter) fall in the category with state, I
reordered the
comment a bit. See below.
> >+ * state which means that the value of one input
sample affects not only
> >one
> >+ * output sample, but some samples before and some
samples after the
>
> s/and/or/ (since that depends on the filter type)
Writing "or" could mean that either some samples
before or some samples
after the corresponding sample are affected, but not both. I
used the
somewhat technical but more precise "and/or" now.
The new version is:
* Most data handles produce output samples from an input
data handle.
* Some of them, like filtering and resampling datahandles,
have an internal
* state which means that the value of one input sample
affects not only one
* output sample, but some samples before and/or some
samples after the
* "corresponding" output sample.
> >+ * "corresponding" output sample.
> >+ *
> >+ * Often the state is symmetric, so that the
number of output samples
> >affected
> >+ * before and after the "corresponding"
output sample is the same. Then
> >the
> >+ * function returns this number. If the state is
asymmetric, this function
> >+ * shall return the maximum of the two numbers.
> >+ *
> >+ * If multiple data handles are cascaded (for
instance when resampling a
>
> hm, "cascaded"? i'd rather say
"nested" (or "chained") here.
Ok: I'll use cascaded.
> >+ * filtered signal), the function propagates the
state length, so that the
> >+ * state of the chain of all operations together
is returned.
>
> "state of the chain"? shouldn't that be
"accumulated state length" or so?
Ok, I'll use "accumulated state length" - it
already sounded somewhat
clumsy to me when I wrote it.
> [...]
>
> >Index: bse/bsedatahandle-resample.cc
>
>========================================================
===========
> >--- bse/bsedatahandle-resample.cc (revision 4068)
> >+++ bse/bsedatahandle-resample.cc (working copy)
> > -136,7 +136,7  protected:
> > }
> >
> > /* implemented by upsampling and downsampling
datahandle */
> >- virtual BseResampler2Mode mode () = 0;
> >+ virtual BseResampler2Mode mode () const = 0;
> > virtual int64 read_frame (int64 frame) = 0;
> >
> >public:
> > -222,18 +222,38  public:
> >
> > return n_values;
> > }
> >+ GslDataHandle*
> >+ get_source() const
> >+ {
> >+ return gsl_data_handle_get_source
(m_src_handle);
> >+ }
> >+ int64
> >+ get_state_length() const
> >+ {
> >+ int64 source_state_length =
gsl_data_handle_get_state_length
> >(m_src_handle);
> >+ if (source_state_length < 0)
> >+ return source_state_length;
>
> huh? why should a datahandle every return a negative
state?
Well, I thought of that line as a similar error propagation
as I do in
read - that is, if the source handle does something strange,
I pass it
on.
What if the source handle had infinite state (like an IIR
filter)? But I
can also simplify it to a g_return_if_fail(), for as long as
there are
no datahandles with non-finite state or othor
"errors".
> >+ if (mode() == BSE_RESAMPLER2_MODE_UPSAMPLE)
> >+ source_state_length *= 2;
> >+ else
> >+ source_state_length = (source_state_length +
1) / 2;
> >+
> >+ if (m_resamplers.size())
> >+ {
> >+ /* For fractional delays, a delay of 10.5 for
instance means that
> >input[0]
> >+ * affects samples 10 and 11 are affected, and
thus the state length
> >we
> >+ * assume for that case is 11.
> >+ */
>
> the comment needs rewording, you use "affect"
two times.
Ok, done.
> >+ int64 per_channel_state = ceil
(m_resamplers[0]->delay());
> >+ return source_state_length + per_channel_state *
> >m_dhandle.setup.n_channels;
> >+ }
> >+ // should never happen
> >+ return -1;
>
> eeeek!
> -1?
> how is that *possibly* a valid state?
>
> note that gsl_data_handle_get_state_length() was not
> defined/discussed/documented as a function that can
possibly fail,
> so why don't you return 0 here?
I don't want to silently return a valid state when something
unpredicted
happens. But I think I can make my preconditions explicit
using
g_return_if_fail(). Like this:
int64
get_state_length() const
{
int64 source_state_length =
gsl_data_handle_get_state_length (m_src_handle);
// m_src_handle must be opened and have valid state size
g_return_val_if_fail (source_state_length >= 0, 0);
if (mode() == BSE_RESAMPLER2_MODE_UPSAMPLE)
source_state_length *= 2;
else
source_state_length = (source_state_length + 1) / 2;
// we must be opened => n_channels > 0, 1
Resampler per Channel
g_return_val_if_fail (!m_resamplers.empty(), 0);
/* For fractional delays, a delay of 10.5 for instance
means that input[0]
* affects samples 10 and 11, and thus the state length
we assume for
* that case is 11.
*/
int64 per_channel_state = ceil
(m_resamplers[0]->delay());
return source_state_length + per_channel_state *
m_dhandle.setup.n_channels;
}
> >+ }
> > static GslDataHandle*
> > dh_create (DataHandleResample2 *cxx_dh)
> > {
> >- static GslDataHandleFuncs dh_vtable =
> >- {
> >- dh_open,
> >- dh_read,
> >- dh_close,
> >- NULL,
> >- dh_destroy,
> >- };
>
> huh?
>
> > if (cxx_dh->m_init_ok)
> > {
> > cxx_dh->m_dhandle.vtable = &dh_vtable;
> > -246,9 +266,10  public:
> > return NULL;
> > }
> > }
> > [...]
> >+
> >+GslDataHandleFuncs DataHandleResample2::dh_vtable
=
> >+{
> >+ dh_open,
> >+ dh_read,
> >+ dh_close,
> >+ dh_get_source,
> >+ dh_get_state_length,
> >+ dh_destroy,
> >};
>
> erk. declaring the table inside the class is not
neccessary
> and defines an additional external symbol. decls should
be
> moved into the innermost scope possible, so please
revert that
> part by moving the table back into dh_create.
Ok.
Here is the new patch:
Index: bse/gsldatahandle-vorbis.c
============================================================
=======
--- bse/gsldatahandle-vorbis.c (revision 4108)
+++ bse/gsldatahandle-vorbis.c (working copy)
 -365,6
+365,7  static GslDataHandleFuncs dh_vorbis_vtab
dh_vorbis_read,
dh_vorbis_close,
NULL,
+ NULL,
dh_vorbis_destroy,
};
Index: bse/gsldatahandle-mad.c
============================================================
=======
--- bse/gsldatahandle-mad.c (revision 4108)
+++ bse/gsldatahandle-mad.c (working copy)
 -673,6
+673,7  static GslDataHandleFuncs dh_mad_vtable
dh_mad_read,
dh_mad_close,
NULL,
+ NULL,
dh_mad_destroy,
};
Index: bse/ChangeLog
============================================================
=======
--- bse/ChangeLog (revision 4108)
+++ bse/ChangeLog (working copy)
 -1,3
+1,19 
+Mon Nov 27 17:53:34 2006 Stefan Westerfeld <stefan space.twc.de>
+
+ * gsldatahandle.[hc]: Added new
gsl_data_handle_get_state_length
+ function for datahandles, with a corresponding vtable
entry. For
+ filtering datahandles (such as lowpass handles), which are
usually
+ stateful, it returns the filter state length.
+
+ * gsldatahandle-vorbis.c:
+ * gsldatahandle-mad.c:
+ * bsedatahandle-resample.cc:
+ * tests/loophandle.c: Implement the get_state_length
datahandle
+ method.
+
+ * tests/resamplehandle.cc: Test the resampler
get_state_length
+ function.
+
Mon Nov 27 15:19:47 2006 Stefan Westerfeld <stefan space.twc.de>
* tests/firhandle.cc: Check that the filter is zero phase
in the
Index: bse/gsldatahandle.c
============================================================
=======
--- bse/gsldatahandle.c (revision 4108)
+++ bse/gsldatahandle.c (working copy)
 -195,6
+195,41  gsl_data_handle_get_source (GslDataHandl
return src_handle;
}
+/**
+ * param data_handle a DataHandle
+ * return the state length of the data handle
+ *
+ * Most data handles produce output samples from an input
data handle.
+ * Some of them, like filtering and resampling datahandles,
have an internal
+ * state which means that the value of one input sample
affects not only one
+ * output sample, but some samples before and/or some
samples after the
+ * "corresponding" output sample.
+ *
+ * Often the state is symmetric, so that the number of
output samples affected
+ * before and after the "corresponding" output
sample is the same. Then the
+ * function returns this number. If the state is
asymmetric, this function
+ * shall return the maximum of the two numbers.
+ *
+ * If multiple data handles are nested (for instance when
resampling a
+ * filtered signal), the function propagates the state
length, so that the
+ * accumulated state length of all operations together is
returned.
+ *
+ * Note: This function can only be used while the data
handle is opened.
+ *
+ * This function is MT-safe and may be called from any
thread.
+ */
+int64
+gsl_data_handle_get_state_length (GslDataHandle *dhandle)
+{
+ g_return_val_if_fail (dhandle != NULL, -1);
+ g_return_val_if_fail (dhandle->open_count > 0, -1);
+
+ GSL_SPIN_LOCK (&dhandle->mutex);
+ int64 state_length =
dhandle->vtable->get_state_length ?
dhandle->vtable->get_state_length (dhandle) : 0;
+ GSL_SPIN_UNLOCK (&dhandle->mutex);
+ return state_length;
+}
+
int64
gsl_data_handle_length (GslDataHandle *dhandle)
{
 -355,6
+390,7  gsl_data_handle_new_mem (guint n
mem_handle_read,
mem_handle_close,
NULL,
+ NULL,
mem_handle_destroy,
};
MemHandle *mhandle;
 -500,6
+536,14  xinfo_get_source_handle (GslDataHandle *
return chandle->src_handle;
}
+static int64
+xinfo_get_state_length (GslDataHandle *dhandle)
+{
+ XInfoHandle *chandle = (XInfoHandle*) dhandle;
+ return gsl_data_handle_get_state_length
(chandle->src_handle);
+}
+
+
static GslDataHandle*
xinfo_data_handle_new (GslDataHandle *src_handle,
gboolean clear_xinfos,
 -511,6
+555,7  xinfo_data_handle_new (GslDataHandle *sr
xinfo_handle_read,
xinfo_handle_close,
xinfo_get_source_handle,
+ xinfo_get_state_length,
xinfo_handle_destroy,
};
SfiRing *dest_added = NULL, *dest_remove = NULL;
 -686,6
+731,12  chain_handle_close (GslDataHandle *dhand
gsl_data_handle_close (chandle->src_handle);
}
+static int64
+chain_handle_get_state_length (GslDataHandle *dhandle)
+{
+ ChainHandle *chandle = (ChainHandle*) dhandle;
+ return gsl_data_handle_get_state_length
(chandle->src_handle);
+}
/* --- reversed handle --- */
static void
 -745,6
+796,7  gsl_data_handle_new_reverse (GslDataHand
reverse_handle_read,
chain_handle_close,
NULL,
+ chain_handle_get_state_length,
reverse_handle_destroy,
};
ReversedHandle *rhandle;
 -853,6
+905,7  gsl_data_handle_new_translate (GslDataHa
cut_handle_read,
chain_handle_close,
NULL,
+ chain_handle_get_state_length,
cut_handle_destroy,
};
CutHandle *chandle;
 -1032,6
+1085,14  insert_handle_read (GslDataHandle *dhand
return orig_n_values - n_values;
}
+static int64
+insert_handle_get_state_length (GslDataHandle *dhandle)
+{
+ InsertHandle *ihandle = (InsertHandle*) dhandle;
+ return gsl_data_handle_get_state_length
(ihandle->src_handle);
+}
+
+
GslDataHandle*
gsl_data_handle_new_insert (GslDataHandle *src_handle,
guint paste_bit_depth,
 -1045,6
+1106,7  gsl_data_handle_new_insert (GslDataHandl
insert_handle_read,
insert_handle_close,
NULL,
+ insert_handle_get_state_length,
insert_handle_destroy,
};
InsertHandle *ihandle;
 -1161,6
+1223,7  gsl_data_handle_new_looped (GslDataHandl
loop_handle_read,
chain_handle_close,
NULL,
+ chain_handle_get_state_length,
loop_handle_destroy,
};
LoopHandle *lhandle;
 -1259,6
+1322,13  dcache_handle_get_source_handle (GslData
return chandle->dcache->dhandle;
}
+static int64
+dcache_handle_get_state_length (GslDataHandle *dhandle)
+{
+ DCacheHandle *chandle = (DCacheHandle*) dhandle;
+ return gsl_data_handle_get_state_length
(chandle->dcache->dhandle);
+}
+
GslDataHandle*
gsl_data_handle_new_dcached (GslDataCache *dcache)
{
 -1267,6
+1337,7  gsl_data_handle_new_dcached (GslDataCach
dcache_handle_read,
dcache_handle_close,
dcache_handle_get_source_handle,
+ dcache_handle_get_state_length,
dcache_handle_destroy,
};
DCacheHandle *dhandle;
 -1495,6
+1566,7  gsl_wave_handle_new (const gchar *f
wave_handle_read,
wave_handle_close,
NULL,
+ NULL,
wave_handle_destroy,
};
WaveHandle *whandle;
Index: bse/gsldatahandle.h
============================================================
=======
--- bse/gsldatahandle.h (revision 4108)
+++ bse/gsldatahandle.h (working copy)
 -63,6
+63,7  struct _GslDataHandleFuncs
gfloat *values);
void (*close) (GslDataHandle *data_handle);
GslDataHandle* (*get_source) (GslDataHandle
*data_handle);
+ int64 (*get_state_length) (GslDataHandle
*data_handle);
void (*destroy) (GslDataHandle *data_handle);
};
 -85,6
+86,7  int64 gsl_data_handle_read (GslDataH
int64 value_offset,
int64 n_values,
gfloat *values);
+int64 gsl_data_handle_get_state_length (GslDataHandle
*dhandle);
GslDataHandle* gsl_data_handle_get_source
(GslDataHandle *dhandle);
GslDataHandle* gsl_data_handle_new_cut (GslDataHandle
*src_handle,
int64 cut_offset,
Index: bse/tests/loophandle.c
============================================================
=======
--- bse/tests/loophandle.c (revision 4108)
+++ bse/tests/loophandle.c (working copy)
 -105,6
+105,14  loop_handle_reference_read (GslDataHandl
}
}
+static int64
+loop_handle_reference_get_state_length (GslDataHandle
*dhandle)
+{
+ LoopHandleReference *lhandle = (LoopHandleReference*)
dhandle;
+ return gsl_data_handle_get_state_length
(lhandle->src_handle);
+}
+
+
static GslDataHandle*
gsl_data_handle_new_looped_reference (GslDataHandle
*src_handle,
GslLong loop_first,
 -115,6
+123,7  gsl_data_handle_new_looped_reference (Gs
loop_handle_reference_read,
loop_handle_reference_close,
NULL,
+ loop_handle_reference_get_state_length,
loop_handle_reference_destroy,
};
LoopHandleReference *lhandle;
Index: bse/tests/resamplehandle.cc
============================================================
=======
--- bse/tests/resamplehandle.cc (revision 4108)
+++ bse/tests/resamplehandle.cc (working copy)
 -371,6
+371,111  test_delay_compensation (const char *run
TDONE();
}
+static void
+test_state_length (const char *run_type)
+{
+ TSTART ("Resampler State Length Info (%s)",
run_type);
+
+
//----------------------------------------------------------
-------------------------
+ // usampling
+
//----------------------------------------------------------
-------------------------
+ {
+ const guint period_size = 107;
+
+ /* fill input with 2 periods of a sine wave, so that
while at the start and
+ * at the end clicks occur (because the unwindowed
signal is assumed to 0 by
+ * the resamplehandle), in the middle 1 period can be
found that is clickless
+ */
+ vector<float> input (period_size * 2);
+ for (size_t i = 0; i < input.size(); i++)
+ input[i] = sin (i * 2 * M_PI / period_size);
+
+ const guint precision_bits = 16;
+ GslDataHandle *ihandle = gsl_data_handle_new_mem (1,
32, 44100, 440, input.size(), &input[0], NULL);
+ GslDataHandle *rhandle = bse_data_handle_new_upsample2
(ihandle, precision_bits);
+ BseErrorType open_error = gsl_data_handle_open
(rhandle);
+ TASSERT (open_error == 0);
+ TASSERT (gsl_data_handle_get_state_length (ihandle) ==
0);
+
+ // determine how much of the end of the signal is
"unusable" due to the resampler state:
+ const int64 state_length =
gsl_data_handle_get_state_length (rhandle);
+
+ /* read resampled signal in the range unaffected by the
resampler state (that
+ * is: not at the directly at the beginning, and not
directly at the end)
+ */
+ vector<float> output (input.size() * 3);
+ for (size_t values_done = 0; values_done <
output.size(); values_done++)
+ {
+ /* NOTE: this is an inlined implementation of a loop,
which you normally would
+ * implement with a loop handle, and it is inefficient
because we read the
+ * samples one-by-one -> usually: don't use such code,
always read in blocks */
+ int64 read_pos = (values_done + state_length) %
(period_size * 2) + (period_size * 2 - state_length);
+ TCHECK (read_pos >= state_length); /* check that
input signal was long enough to be for this test */
+ int64 values_read = gsl_data_handle_read (rhandle,
read_pos, 1, &output[values_done]);
+ TCHECK (values_read == 1);
+ }
+ double error = 0;
+ for (size_t i = 0; i < output.size(); i++)
+ {
+ double expected = sin (i * 2 * M_PI / (period_size * 2));
+ error = MAX (error, fabs (output[i] - expected));
+ }
+ double error_db = bse_db_from_factor (error, -200);
+ TASSERT (error_db < -97);
+ }
+
+
//----------------------------------------------------------
-------------------------
+ // downsampling
+
//----------------------------------------------------------
-------------------------
+
+ {
+ const guint period_size = 190;
+
+ /* fill input with 2 periods of a sine wave, so that
while at the start and
+ * at the end clicks occur (because the unwindowed
signal is assumed to 0 by
+ * the resamplehandle), in the middle 1 period can be
found that is clickless
+ */
+ vector<float> input (period_size * 2);
+ for (size_t i = 0; i < input.size(); i++)
+ input[i] = sin (i * 2 * M_PI / period_size);
+
+ const guint precision_bits = 16;
+ GslDataHandle *ihandle = gsl_data_handle_new_mem (1,
32, 44100, 440, input.size(), &input[0], NULL);
+ GslDataHandle *rhandle =
bse_data_handle_new_downsample2 (ihandle, precision_bits);
+ BseErrorType open_error = gsl_data_handle_open
(rhandle);
+ TASSERT (open_error == 0);
+ TASSERT (gsl_data_handle_get_state_length (ihandle) ==
0);
+
+ // determine how much of the end of the signal is
"unusable" due to the resampler state:
+ const int64 state_length =
gsl_data_handle_get_state_length (rhandle);
+
+ /* read resampled signal in the range unaffected by the
resampler state (that
+ * is: not at the directly at the beginning, and not
directly at the end)
+ */
+ vector<float> output (input.size() * 3 / 2);
+ for (size_t values_done = 0; values_done <
output.size(); values_done++)
+ {
+ /* NOTE: this is an inlined implementation of a loop,
which you normally would
+ * implement with a loop handle, and it is inefficient
because we read the
+ * samples one-by-one -> usually: don't use such code,
always read in blocks */
+ int64 read_pos = (values_done + state_length) %
(period_size / 2) + (period_size / 2 - state_length);
+ TCHECK (read_pos >= state_length); /* check that
input signal was long enough to be for this test */
+ int64 values_read = gsl_data_handle_read (rhandle,
read_pos, 1, &output[values_done]);
+ TCHECK (values_read == 1);
+ }
+ double error = 0;
+ for (size_t i = 0; i < output.size(); i++)
+ {
+ double expected = sin (i * 2 * M_PI / (period_size / 2));
+ error = MAX (error, fabs (output[i] - expected));
+ }
+ double error_db = bse_db_from_factor (error, -200);
+ TASSERT (error_db < -105);
+ }
+ TDONE();
+}
+
+
int
main (int argc,
char *argv[])
 -385,6
+490,7  main (int argc,
test_c_api ("FPU");
test_delay_compensation ("FPU");
+ test_state_length ("FPU");
run_tests ("FPU");
/* load plugins */
 -399,6
+505,7  main (int argc,
test_c_api ("SSE");
test_delay_compensation ("SSE");
+ test_state_length ("SSE");
run_tests ("SSE");
return 0;
Index: bse/bsedatahandle-resample.cc
============================================================
=======
--- bse/bsedatahandle-resample.cc (revision 4108)
+++ bse/bsedatahandle-resample.cc (working copy)
 -137,7
+137,7  protected:
}
/* implemented by upsampling and downsampling datahandle
*/
- virtual BseResampler2Mode mode () = 0;
+ virtual BseResampler2Mode mode () const = 0;
virtual int64 read_frame (int64 frame) = 0;
public:
 -238,7
+238,28  public:
return n_values;
}
+ int64
+ get_state_length() const
+ {
+ int64 source_state_length =
gsl_data_handle_get_state_length (m_src_handle);
+ // m_src_handle must be opened and have valid state
size
+ g_return_val_if_fail (source_state_length >= 0, 0);
+ if (mode() == BSE_RESAMPLER2_MODE_UPSAMPLE)
+ source_state_length *= 2;
+ else
+ source_state_length = (source_state_length + 1) / 2;
+
+ // we must be opened => n_channels > 0, 1
Resampler per Channel
+ g_return_val_if_fail (!m_resamplers.empty(), 0);
+
+ /* For fractional delays, a delay of 10.5 for instance
means that input[0]
+ * affects samples 10 and 11, and thus the state length
we assume for
+ * that case is 11.
+ */
+ int64 per_channel_state = ceil
(m_resamplers[0]->delay());
+ return source_state_length + per_channel_state *
m_dhandle.setup.n_channels;
+ }
static GslDataHandle*
dh_create (DataHandleResample2 *cxx_dh)
{
 -248,8
+269,10  public:
dh_read,
dh_close,
NULL,
+ dh_get_state_length,
dh_destroy,
};
+
if (cxx_dh->m_init_ok)
{
cxx_dh->m_dhandle.vtable = &dh_vtable;
 -262,7
+285,6  public:
return NULL;
}
}
-
private:
/* for the "C" API (vtable) */
static DataHandleResample2*
 -294,6
+316,11  private:
{
return dh_cast (dhandle)->read (voffset, n_values,
values);
}
+ static int64
+ dh_get_state_length (GslDataHandle *dhandle)
+ {
+ return dh_cast (dhandle)->get_state_length();
+ }
};
class DataHandleUpsample2 : public DataHandleResample2
 -307,7
+334,7  public:
m_dhandle.name = g_strconcat (m_src_handle->name,
"// #upsample2 /", NULL);
}
BseResampler2Mode
- mode()
+ mode() const
{
return BSE_RESAMPLER2_MODE_UPSAMPLE;
}
 -382,7
+409,7  public:
{
}
BseResampler2Mode
- mode()
+ mode() const
{
return BSE_RESAMPLER2_MODE_DOWNSAMPLE;
}
Cu... Stefan
--
Stefan Westerfeld, Hamburg/Germany, http://space.twc.de/~stef
an
_______________________________________________
beast mailing list
beast gnome.org
http://m
ail.gnome.org/mailman/listinfo/beast
|
|
| Datahandle State API changes |

|
2006-11-27 17:35:51 |
On Mon, 27 Nov 2006, Stefan Westerfeld wrote:
> On Sat, Nov 25, 2006 at 05:48:51PM +0100, Tim Janik
wrote:
>> On Mon, 6 Nov 2006, Stefan Westerfeld wrote:
>>> + * state which means that the value of one
input sample affects not only
>>> one
>>> + * output sample, but some samples before and
some samples after the
>>
>> s/and/or/ (since that depends on the filter type)
>
> Writing "or" could mean that either some
samples before or some samples
> after the corresponding sample are affected, but not
both.
nope, in natural language, "or" can mean exclusive
or and non-exclusive or,
but i'm still fine with your new wording.
>>> + * "corresponding" output sample.
>>> + *
>>> + * Often the state is symmetric, so that the
number of output samples
>>> affected
>>> + * before and after the
"corresponding" output sample is the same. Then
>>> the
>>> + * function returns this number. If the state
is asymmetric, this function
>>> + * shall return the maximum of the two
numbers.
>>> + *
>>> + * If multiple data handles are cascaded (for
instance when resampling a
>>
>> hm, "cascaded"? i'd rather say
"nested" (or "chained") here.
>
> Ok: I'll use cascaded.
i don't think "cascaded" is accurate here.
cascaded handles could mean
using handles in parallel (e.g. you could line up 6 handles
in a specifically
ordered cascade to produce a 5.1 dolby handle ;)
only "nested" and to some extend
"chained" carry across the sequential
ordering of the handles.
>>> Index: bse/bsedatahandle-resample.cc
>>>
============================================================
=======
>>> --- bse/bsedatahandle-resample.cc (revision
4068)
>>> +++ bse/bsedatahandle-resample.cc (working
copy)
>>>  -136,7 +136,7  protected:
>>> }
>>>
>>> /* implemented by upsampling and downsampling
datahandle */
>>> - virtual BseResampler2Mode mode () = 0;
>>> + virtual BseResampler2Mode mode () const = 0;
>>> virtual int64 read_frame (int64 frame) =
0;
>>>
>>> public:
>>>  -222,18 +222,38  public:
>>>
>>> return n_values;
>>> }
>>> + GslDataHandle*
>>> + get_source() const
>>> + {
>>> + return gsl_data_handle_get_source
(m_src_handle);
>>> + }
>>> + int64
>>> + get_state_length() const
>>> + {
>>> + int64 source_state_length =
gsl_data_handle_get_state_length
>>> (m_src_handle);
>>> + if (source_state_length < 0)
>>> + return source_state_length;
>>
>> huh? why should a datahandle every return a
negative state?
>
> Well, I thought of that line as a similar error
propagation as I do in
> read - that is, if the source handle does something
strange, I pass it
> on.
>
> What if the source handle had infinite state (like an
IIR filter)?
then we return that, the best approximation would be
G_MAXINT64.
> But I
> can also simplify it to a g_return_if_fail(), for as
long as there are
> no datahandles with non-finite state or othor
"errors".
as i said previously, there's no "error" handling
defined for this function.
> Here is the new patch:
just pasting the changed portions would have been enough,
it's not like the
patch needed a major overhaul here. provided the above stuff
is fixed it
should be ready to go in i think.
> Cu... Stefan
---
ciaoTJ
_______________________________________________
beast mailing list
beast gnome.org
http://m
ail.gnome.org/mailman/listinfo/beast
|
|
| Datahandle State API changes |

|
2006-11-27 19:47:23 |
Hi!
On Mon, Nov 27, 2006 at 06:35:51PM +0100, Tim Janik wrote:
> >>>+ * Often the state is symmetric, so that
the number of output samples
> >>>affected
> >>>+ * before and after the
"corresponding" output sample is the same. Then
> >>>the
> >>>+ * function returns this number. If the
state is asymmetric, this
> >>>function
> >>>+ * shall return the maximum of the two
numbers.
> >>>+ *
> >>>+ * If multiple data handles are cascaded
(for instance when resampling a
> >>
> >>hm, "cascaded"? i'd rather say
"nested" (or "chained") here.
> >
> >Ok: I'll use cascaded.
>
> i don't think "cascaded" is accurate here.
cascaded handles could mean
> using handles in parallel (e.g. you could line up 6
handles in a
> specifically
> ordered cascade to produce a 5.1 dolby handle ;)
> only "nested" and to some extend
"chained" carry across the sequential
> ordering of the handles.
Well, "nested" then (it was in the nek patch
alteady, just the remark in
md mail was wrong). Btw. your mono -> 5.1 transform will
have to
implement get_state, too.
> >>>Index: bse/bsedatahandle-resample.cc
>
>>>================================================
===================
> >>>--- bse/bsedatahandle-resample.cc (revision
4068)
> >>>+++ bse/bsedatahandle-resample.cc (working
copy)
> >>> -136,7 +136,7  protected:
> >>> }
> >>>
> >>> /* implemented by upsampling and
downsampling datahandle */
> >>>- virtual BseResampler2Mode mode () = 0;
> >>>+ virtual BseResampler2Mode mode () const
= 0;
> >>> virtual int64 read_frame (int64
frame) = 0;
> >>>
> >>>public:
> >>> -222,18 +222,38  public:
> >>>
> >>> return n_values;
> >>> }
> >>>+ GslDataHandle*
> >>>+ get_source() const
> >>>+ {
> >>>+ return gsl_data_handle_get_source
(m_src_handle);
> >>>+ }
> >>>+ int64
> >>>+ get_state_length() const
> >>>+ {
> >>>+ int64 source_state_length =
gsl_data_handle_get_state_length
> >>>(m_src_handle);
> >>>+ if (source_state_length < 0)
> >>>+ return source_state_length;
> >>
> >>huh? why should a datahandle every return a
negative state?
> >
> >Well, I thought of that line as a similar error
propagation as I do in
> >read - that is, if the source handle does something
strange, I pass it
> >on.
> >
> >What if the source handle had infinite state (like
an IIR filter)?
>
> then we return that, the best approximation would be
G_MAXINT64.
When we introduce it, we will have to specialcase every
computation with
states, as 2 * G_MAXINT64 and G_MAXINT64 + 31 is not
G_MAXINT64. Such
computations can be found in the resampler already (and will
probably
occur elsewhere).
So for now the code doesn't give G_MAXINT64 a special
meaning.
> >Here is the new patch:
>
> just pasting the changed portions would have been
enough, it's not like the
> patch needed a major overhaul here. provided the above
stuff is fixed it
> should be ready to go in i think.
Committed.
Cu... Stefan
--
Stefan Westerfeld, Hamburg/Germany, http://space.twc.de/~stef
an
_______________________________________________
beast mailing list
beast gnome.org
http://m
ail.gnome.org/mailman/listinfo/beast
|
|
[1-5]
|
|