|
View:
New views
5 Messages
—
Rating Filter:
Alert me
|
|
|
Datahandle State API changes 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@...> + + * 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@...> * 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/~stefan _______________________________________________ beast mailing list beast@... http://mail.gnome.org/mailman/listinfo/beast |
|
|
Re: Datahandle State API changesOn 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@...> > + > + * 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@...> > > * 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@... http://mail.gnome.org/mailman/listinfo/beast |
|
|
Re: Datahandle State API changes 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@...> + + * 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@...> * 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/~stefan _______________________________________________ beast mailing list beast@... http://mail.gnome.org/mailman/listinfo/beast |
|
|
Re: Datahandle State API changesOn 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@... http://mail.gnome.org/mailman/listinfo/beast |
|
|
Re: Datahandle State API changes 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/~stefan _______________________________________________ beast mailing list beast@... http://mail.gnome.org/mailman/listinfo/beast |
| Free embeddable forum powered by Nabble | Forum Help |