r4109 - in trunk/bse: . tests
- From: stw svn gnome org
- To: svn-commits-list gnome org
- Subject: r4109 - in trunk/bse: . tests
- Date: Mon, 27 Nov 2006 14:31:46 -0500 (EST)
Author: stw
Date: 2006-11-27 14:31:25 -0500 (Mon, 27 Nov 2006)
New Revision: 4109
Modified:
trunk/bse/ChangeLog
trunk/bse/bsedatahandle-resample.cc
trunk/bse/gsldatahandle-mad.c
trunk/bse/gsldatahandle-vorbis.c
trunk/bse/gsldatahandle.c
trunk/bse/gsldatahandle.h
trunk/bse/tests/loophandle.c
trunk/bse/tests/resamplehandle.cc
Log:
Mon Nov 27 20:29:53 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.
Modified: trunk/bse/ChangeLog
===================================================================
--- trunk/bse/ChangeLog 2006-11-27 14:24:02 UTC (rev 4108)
+++ trunk/bse/ChangeLog 2006-11-27 19:31:25 UTC (rev 4109)
@@ -1,3 +1,19 @@
+Mon Nov 27 20:29:53 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
Modified: trunk/bse/bsedatahandle-resample.cc
===================================================================
--- trunk/bse/bsedatahandle-resample.cc 2006-11-27 14:24:02 UTC (rev 4108)
+++ trunk/bse/bsedatahandle-resample.cc 2006-11-27 19:31:25 UTC (rev 4109)
@@ -137,7 +137,7 @@
}
/* 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 @@
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 @@
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 @@
return NULL;
}
}
-
private:
/* for the "C" API (vtable) */
static DataHandleResample2*
@@ -294,6 +316,11 @@
{
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 @@
m_dhandle.name = g_strconcat (m_src_handle->name, "// #upsample2 /", NULL);
}
BseResampler2Mode
- mode()
+ mode() const
{
return BSE_RESAMPLER2_MODE_UPSAMPLE;
}
@@ -382,7 +409,7 @@
{
}
BseResampler2Mode
- mode()
+ mode() const
{
return BSE_RESAMPLER2_MODE_DOWNSAMPLE;
}
Modified: trunk/bse/gsldatahandle-mad.c
===================================================================
--- trunk/bse/gsldatahandle-mad.c 2006-11-27 14:24:02 UTC (rev 4108)
+++ trunk/bse/gsldatahandle-mad.c 2006-11-27 19:31:25 UTC (rev 4109)
@@ -673,6 +673,7 @@
dh_mad_read,
dh_mad_close,
NULL,
+ NULL,
dh_mad_destroy,
};
Modified: trunk/bse/gsldatahandle-vorbis.c
===================================================================
--- trunk/bse/gsldatahandle-vorbis.c 2006-11-27 14:24:02 UTC (rev 4108)
+++ trunk/bse/gsldatahandle-vorbis.c 2006-11-27 19:31:25 UTC (rev 4109)
@@ -365,6 +365,7 @@
dh_vorbis_read,
dh_vorbis_close,
NULL,
+ NULL,
dh_vorbis_destroy,
};
Modified: trunk/bse/gsldatahandle.c
===================================================================
--- trunk/bse/gsldatahandle.c 2006-11-27 14:24:02 UTC (rev 4108)
+++ trunk/bse/gsldatahandle.c 2006-11-27 19:31:25 UTC (rev 4109)
@@ -195,7 +195,42 @@
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)
{
int64 l;
@@ -355,6 +390,7 @@
mem_handle_read,
mem_handle_close,
NULL,
+ NULL,
mem_handle_destroy,
};
MemHandle *mhandle;
@@ -500,6 +536,14 @@
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_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 @@
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 @@
reverse_handle_read,
chain_handle_close,
NULL,
+ chain_handle_get_state_length,
reverse_handle_destroy,
};
ReversedHandle *rhandle;
@@ -853,6 +905,7 @@
cut_handle_read,
chain_handle_close,
NULL,
+ chain_handle_get_state_length,
cut_handle_destroy,
};
CutHandle *chandle;
@@ -1032,6 +1085,14 @@
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 @@
insert_handle_read,
insert_handle_close,
NULL,
+ insert_handle_get_state_length,
insert_handle_destroy,
};
InsertHandle *ihandle;
@@ -1161,6 +1223,7 @@
loop_handle_read,
chain_handle_close,
NULL,
+ chain_handle_get_state_length,
loop_handle_destroy,
};
LoopHandle *lhandle;
@@ -1259,6 +1322,13 @@
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 @@
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 @@
wave_handle_read,
wave_handle_close,
NULL,
+ NULL,
wave_handle_destroy,
};
WaveHandle *whandle;
Modified: trunk/bse/gsldatahandle.h
===================================================================
--- trunk/bse/gsldatahandle.h 2006-11-27 14:24:02 UTC (rev 4108)
+++ trunk/bse/gsldatahandle.h 2006-11-27 19:31:25 UTC (rev 4109)
@@ -63,60 +63,62 @@
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);
};
/* --- standard functions --- */
-GslDataHandle* gsl_data_handle_ref (GslDataHandle *dhandle);
-void gsl_data_handle_unref (GslDataHandle *dhandle);
-BseErrorType gsl_data_handle_open (GslDataHandle *dhandle);
-void gsl_data_handle_close (GslDataHandle *dhandle);
-int64 gsl_data_handle_length (GslDataHandle *data_handle);
-#define gsl_data_handle_n_values( dh) \
- gsl_data_handle_length (dh)
-guint gsl_data_handle_n_channels (GslDataHandle *data_handle);
-guint gsl_data_handle_bit_depth (GslDataHandle *data_handle);
-gfloat gsl_data_handle_mix_freq (GslDataHandle *data_handle);
-gfloat gsl_data_handle_osc_freq (GslDataHandle *data_handle);
-const gchar* gsl_data_handle_name (GslDataHandle *data_handle);
-int64 gsl_data_handle_read (GslDataHandle *data_handle,
- int64 value_offset,
- int64 n_values,
- gfloat *values);
-GslDataHandle* gsl_data_handle_get_source (GslDataHandle *dhandle);
-GslDataHandle* gsl_data_handle_new_cut (GslDataHandle *src_handle,
- int64 cut_offset,
- int64 n_cut_values);
-GslDataHandle* gsl_data_handle_new_crop (GslDataHandle *src_handle,
- int64 n_head_cut,
- int64 n_tail_cut);
-GslDataHandle* gsl_data_handle_new_reverse (GslDataHandle *src_handle);
-GslDataHandle* gsl_data_handle_new_insert (GslDataHandle *src_handle,
- guint pasted_bit_depth,
- int64 insertion_offset,
- int64 n_paste_values,
- const gfloat *paste_values,
- void (*free) (gpointer values));
-GslDataHandle* gsl_data_handle_new_mem (guint n_channels,
- guint bit_depth,
- gfloat mix_freq,
- gfloat osc_freq,
- int64 n_values,
- const gfloat *values,
- void (*free) (gpointer values));
-GslDataHandle* gsl_data_handle_new_dcached (GslDataCache *dcache);
+GslDataHandle* gsl_data_handle_ref (GslDataHandle *dhandle);
+void gsl_data_handle_unref (GslDataHandle *dhandle);
+BseErrorType gsl_data_handle_open (GslDataHandle *dhandle);
+void gsl_data_handle_close (GslDataHandle *dhandle);
+int64 gsl_data_handle_length (GslDataHandle *data_handle);
+#define gsl_data_handle_n_values( dh) \
+ gsl_data_handle_length (dh)
+guint gsl_data_handle_n_channels (GslDataHandle *data_handle);
+guint gsl_data_handle_bit_depth (GslDataHandle *data_handle);
+gfloat gsl_data_handle_mix_freq (GslDataHandle *data_handle);
+gfloat gsl_data_handle_osc_freq (GslDataHandle *data_handle);
+const gchar* gsl_data_handle_name (GslDataHandle *data_handle);
+int64 gsl_data_handle_read (GslDataHandle *data_handle,
+ 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,
+ int64 n_cut_values);
+GslDataHandle* gsl_data_handle_new_crop (GslDataHandle *src_handle,
+ int64 n_head_cut,
+ int64 n_tail_cut);
+GslDataHandle* gsl_data_handle_new_reverse (GslDataHandle *src_handle);
+GslDataHandle* gsl_data_handle_new_insert (GslDataHandle *src_handle,
+ guint pasted_bit_depth,
+ int64 insertion_offset,
+ int64 n_paste_values,
+ const gfloat *paste_values,
+ void (*free) (gpointer values));
+GslDataHandle* gsl_data_handle_new_mem (guint n_channels,
+ guint bit_depth,
+ gfloat mix_freq,
+ gfloat osc_freq,
+ int64 n_values,
+ const gfloat *values,
+ void (*free) (gpointer values));
+GslDataHandle* gsl_data_handle_new_dcached (GslDataCache *dcache);
/* cheap and inefficient, testpurpose only */
-GslDataHandle* gsl_data_handle_new_looped (GslDataHandle *src_handle,
- int64 loop_first,
- int64 loop_last);
+GslDataHandle* gsl_data_handle_new_looped (GslDataHandle *src_handle,
+ int64 loop_first,
+ int64 loop_last);
/* --- factor 2 resampling datahandles --- */
-GslDataHandle* bse_data_handle_new_upsample2 (GslDataHandle *src_handle, // implemented in bsedatahandle-resample.cc
- int precision_bits);
-GslDataHandle* bse_data_handle_new_downsample2 (GslDataHandle *src_handle,
- int precision_bits); // implemented in bsedatahandle-resample.cc
+GslDataHandle* bse_data_handle_new_upsample2 (GslDataHandle *src_handle, // implemented in bsedatahandle-resample.cc
+ int precision_bits);
+GslDataHandle* bse_data_handle_new_downsample2 (GslDataHandle *src_handle,
+ int precision_bits); // implemented in bsedatahandle-resample.cc
GslDataHandle* gsl_data_handle_new_fir_highpass (GslDataHandle *src_handle, // implemented in bsedatahandle-fir.cc
gdouble cutoff_freq,
@@ -124,11 +126,11 @@
/* --- xinfo handling --- */
-GslDataHandle* gsl_data_handle_new_add_xinfos (GslDataHandle *src_handle,
- gchar **xinfos);
-GslDataHandle* gsl_data_handle_new_remove_xinfos (GslDataHandle *src_handle,
- gchar **xinfos);
-GslDataHandle* gsl_data_handle_new_clear_xinfos (GslDataHandle *src_handle);
+GslDataHandle* gsl_data_handle_new_add_xinfos (GslDataHandle *src_handle,
+ gchar **xinfos);
+GslDataHandle* gsl_data_handle_new_remove_xinfos (GslDataHandle *src_handle,
+ gchar **xinfos);
+GslDataHandle* gsl_data_handle_new_clear_xinfos (GslDataHandle *src_handle);
/* --- wave specific functions --- */
Modified: trunk/bse/tests/loophandle.c
===================================================================
--- trunk/bse/tests/loophandle.c 2006-11-27 14:24:02 UTC (rev 4108)
+++ trunk/bse/tests/loophandle.c 2006-11-27 19:31:25 UTC (rev 4109)
@@ -82,9 +82,9 @@
static GslLong
loop_handle_reference_read (GslDataHandle *dhandle,
- GslLong voffset,
- GslLong n_values,
- gfloat *values)
+ GslLong voffset,
+ GslLong n_values,
+ gfloat *values)
{
LoopHandleReference *lhandle = (LoopHandleReference*) dhandle;
@@ -105,6 +105,14 @@
}
}
+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 @@
loop_handle_reference_read,
loop_handle_reference_close,
NULL,
+ loop_handle_reference_get_state_length,
loop_handle_reference_destroy,
};
LoopHandleReference *lhandle;
Modified: trunk/bse/tests/resamplehandle.cc
===================================================================
--- trunk/bse/tests/resamplehandle.cc 2006-11-27 14:24:02 UTC (rev 4108)
+++ trunk/bse/tests/resamplehandle.cc 2006-11-27 19:31:25 UTC (rev 4109)
@@ -371,6 +371,111 @@
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 @@
test_c_api ("FPU");
test_delay_compensation ("FPU");
+ test_state_length ("FPU");
run_tests ("FPU");
/* load plugins */
@@ -399,6 +505,7 @@
test_c_api ("SSE");
test_delay_compensation ("SSE");
+ test_state_length ("SSE");
run_tests ("SSE");
return 0;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]