chiark / gitweb /
Trivial resampler fixes
authorRichard Kettlewell <rjk@greenend.org.uk>
Thu, 19 Nov 2009 20:07:40 +0000 (20:07 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Thu, 19 Nov 2009 20:07:40 +0000 (20:07 +0000)
Scanty resampler testing

.bzrignore
lib/resample.c
lib/resample.h
libtests/Makefile.am
libtests/t-resample.c [new file with mode: 0644]

index 3b13be6a0c822c34e9e020f263dbd13a9a8e88bf..7695a2b191cfa7586f17bb623f906b3266b6cd6c 100644 (file)
@@ -203,3 +203,4 @@ doc/disorder-choose.8.html
 config.aux/compile
 server/endian
 clients/rtpmon
 config.aux/compile
 server/endian
 clients/rtpmon
+libtests/t-resample
index 9617db9fb6e292f6312c5b70aedd0722502a67fb..de2358cdbfbdf7339f7b464a1efb4cdfa1434c26 100644 (file)
@@ -105,17 +105,17 @@ void resample_close(struct resampler *rs) {
  * @param where Where to store result
  * @return Number of bytes consumed
  */
  * @param where Where to store result
  * @return Number of bytes consumed
  */
-static size_t resample_get_sample(struct resampler *rs,
+static size_t resample_get_sample(const struct resampler *rs,
                                   const uint8_t *bytes,
                                   float *where) {
   switch(rs->input_bits + rs->input_signed + rs->input_endian) {
   case 8+ENDIAN_BIG:
   case 8+ENDIAN_LITTLE:
                                   const uint8_t *bytes,
                                   float *where) {
   switch(rs->input_bits + rs->input_signed + rs->input_endian) {
   case 8+ENDIAN_BIG:
   case 8+ENDIAN_LITTLE:
-    *where = (bytes[0] - 128)/ 128;
+    *where = (bytes[0] - 128)/ 128.0;
     return 1;
   case 8+SIGNED+ENDIAN_BIG:
   case 8+SIGNED+ENDIAN_LITTLE:
     return 1;
   case 8+SIGNED+ENDIAN_BIG:
   case 8+SIGNED+ENDIAN_LITTLE:
-    *where = (int8_t)bytes[0] / 128;
+    *where = (int8_t)bytes[0] / 128.0;
     return 1;
   case 16+ENDIAN_BIG:
     *where = (bytes[0] * 256 + bytes[1] - 32768)/ 32768.0;
     return 1;
   case 16+ENDIAN_BIG:
     *where = (bytes[0] * 256 + bytes[1] - 32768)/ 32768.0;
@@ -156,11 +156,11 @@ static inline int clip(int n, int min, int max) {
  *
  * The value is clipped naively if it will not fit.
  */
  *
  * The value is clipped naively if it will not fit.
  */
-static size_t resample_put_sample(struct resampler *rs,
+static size_t resample_put_sample(const struct resampler *rs,
                                   float sample,
                                   uint8_t *bytes) {
   unsigned value;
                                   float sample,
                                   uint8_t *bytes) {
   unsigned value;
-  switch(rs->input_bits + rs->input_signed + rs->input_endian) {
+  switch(rs->output_bits + rs->output_signed + rs->output_endian) {
   case 8+ENDIAN_BIG:
   case 8+ENDIAN_LITTLE:
     *bytes = clip(sample * 128.0 + 128, 0, 255);
   case 8+ENDIAN_BIG:
   case 8+ENDIAN_LITTLE:
     *bytes = clip(sample * 128.0 + 128, 0, 255);
@@ -210,7 +210,7 @@ static size_t resample_put_sample(struct resampler *rs,
  * the input either mono or stereo, so the result isn't actually going to be
  * too bad.
  */
  * the input either mono or stereo, so the result isn't actually going to be
  * too bad.
  */
-static void resample_prepare_input(struct resampler *rs,
+static void resample_prepare_input(const struct resampler *rs,
                                    const uint8_t *bytes,
                                    size_t nbytes,
                                    float *floats) {
                                    const uint8_t *bytes,
                                    size_t nbytes,
                                    float *floats) {
@@ -233,6 +233,7 @@ static void resample_prepare_input(struct resampler *rs,
         ++floats;
       }
     }
         ++floats;
       }
     }
+    --nframes;
   }
 }
 
   }
 }
 
@@ -242,14 +243,17 @@ static void resample_prepare_input(struct resampler *rs,
  * @param nbytes Number of bytes to convert
  * @param eof Set an end of input stream
  * @param converted Called with converted data (possibly more than once)
  * @param nbytes Number of bytes to convert
  * @param eof Set an end of input stream
  * @param converted Called with converted data (possibly more than once)
+ * @param cd Passed to @p cd
  * @return Number of bytes consumed
  */
  * @return Number of bytes consumed
  */
-size_t resample_convert(struct resampler *rs,
+size_t resample_convert(const struct resampler *rs,
                         const uint8_t *bytes,
                         size_t nbytes,
                         int eof,
                         void (*converted)(uint8_t *bytes,
                         const uint8_t *bytes,
                         size_t nbytes,
                         int eof,
                         void (*converted)(uint8_t *bytes,
-                                          size_t nbytes)) {
+                                          size_t nbytes,
+                                          void *cd),
+                        void *cd) {
   size_t nframesin = nbytes / (rs->input_bytes_per_frame);
   size_t nsamplesout;
   float *input = xcalloc(nframesin * rs->output_channels, sizeof (float));
   size_t nframesin = nbytes / (rs->input_bytes_per_frame);
   size_t nsamplesout;
   float *input = xcalloc(nframesin * rs->output_channels, sizeof (float));
@@ -290,7 +294,7 @@ size_t resample_convert(struct resampler *rs,
       bufused += resample_put_sample(rs, *op++, buffer + bufused);
       --nsamplesout;
     }
       bufused += resample_put_sample(rs, *op++, buffer + bufused);
       --nsamplesout;
     }
-    converted(buffer, bufused);
+    converted(buffer, bufused, cd);
   }
   if(output != input)
     xfree(output);
   }
   if(output != input)
     xfree(output);
index e8bfb10b2e84c11f21f32778e50b3fea88efe333..477efd9285962f0f2fefc737475e7ab264b142e1 100644 (file)
@@ -47,12 +47,14 @@ void resample_init(struct resampler *rs,
                    int output_bits, int output_channels, 
                    int output_rate, int output_signed,
                    int output_endian);
                    int output_bits, int output_channels, 
                    int output_rate, int output_signed,
                    int output_endian);
-size_t resample_convert(struct resampler *rs,
+size_t resample_convert(const struct resampler *rs,
                         const uint8_t *bytes,
                         size_t nbytes,
                         int eof,
                         void (*converted)(uint8_t *bytes,
                         const uint8_t *bytes,
                         size_t nbytes,
                         int eof,
                         void (*converted)(uint8_t *bytes,
-                                          size_t nbytes));
+                                          size_t nbytes,
+                                          void *cd),
+                        void *cd);
 void resample_close(struct resampler *rs);
 
 #endif /* RESAMPLE_H */
 void resample_close(struct resampler *rs);
 
 #endif /* RESAMPLE_H */
index 2f81f20bbb02ccf247850566c2e6ed01471da897..7be6c510063f1ebfdaf388969523409b7c5e5087 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
 #
 # This file is part of DisOrder.
-# Copyright (C) 2008 Richard Kettlewell
+# Copyright (C) 2009 Richard Kettlewell
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@ TESTS=t-addr t-arcfour t-basen t-bits t-cache t-casefold t-charset    \
        t-cookies t-dateparse t-event t-filepart t-hash t-heap t-hex    \
        t-kvp t-mime t-printf t-regsub t-selection t-signame t-sink     \
        t-split t-syscalls t-trackname t-unicode t-url t-utf8 t-vector  \
        t-cookies t-dateparse t-event t-filepart t-hash t-heap t-hex    \
        t-kvp t-mime t-printf t-regsub t-selection t-signame t-sink     \
        t-split t-syscalls t-trackname t-unicode t-url t-utf8 t-vector  \
-       t-words t-wstat t-macros t-cgi t-eventdist
+       t-words t-wstat t-macros t-cgi t-eventdist t-resample
 
 noinst_PROGRAMS=$(TESTS)
 
 
 noinst_PROGRAMS=$(TESTS)
 
@@ -61,6 +61,8 @@ t_vector_SOURCES=t-vector.c test.c test.h
 t_words_SOURCES=t-words.c test.c test.h
 t_wstat_SOURCES=t-wstat.c test.c test.h
 t_eventdist_SOURCES=t-eventdist.c test.c test.h
 t_words_SOURCES=t-words.c test.c test.h
 t_wstat_SOURCES=t-wstat.c test.c test.h
 t_eventdist_SOURCES=t-eventdist.c test.c test.h
+t_resample_SOURCES=t-resample.c test.c test.h
+t_resample_LDFLAGS=$(LIBSAMPLERATE)
 
 check-report: before-check check make-coverage-reports
 before-check:
 
 check-report: before-check check make-coverage-reports
 before-check:
diff --git a/libtests/t-resample.c b/libtests/t-resample.c
new file mode 100644 (file)
index 0000000..1dbdf8b
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "test.h"
+#include "resample.h"
+#include "vector.h"
+
+/* Accumulate converted bytes in a dynamic string */
+static void converted(uint8_t *bytes,
+                      size_t nbytes,
+                      void *cd) {
+  struct dynstr *d = cd;
+  dynstr_append_bytes(d, (void *)bytes, nbytes);
+}
+
+/* Converter wrapper */
+static uint8_t *convert(const struct resampler *rs,
+                        const uint8_t *input, size_t input_bytes,
+                        size_t *output_bytes) {
+  struct dynstr d[1];
+
+  dynstr_init(d);
+  while(input_bytes > 0) {
+    size_t chunk = input_bytes > 1024 ? 1024 : input_bytes;
+    size_t consumed = resample_convert(rs,
+                                       input, input_bytes,
+                                       input_bytes == chunk,
+                                       converted,
+                                       d);
+    input += consumed;
+    input_bytes -= consumed;
+  }
+  *output_bytes = d->nvec;
+  return (uint8_t *)d->vec;
+}
+
+static const uint8_t simple_bytes_u[] = {
+  0, 127, 128, 255,
+};
+
+static const uint8_t simple_bytes_s[] = {
+  -128, -1, 0, 127,
+};
+
+static const struct {
+  const char *description;
+  int input_bits;
+  int input_channels;
+  int input_rate;
+  int input_signed;
+  int input_endian;
+  const uint8_t *input;
+  size_t input_bytes;
+  int output_bits;
+  int output_channels;
+  int output_rate;
+  int output_signed;
+  int output_endian;
+  const uint8_t *output;
+  size_t output_bytes;
+} conversions[] = {
+  /* Conversions that don't change the sample rate */
+  {
+    "empty input",
+    8, 1, 8000, 0, ENDIAN_LITTLE, (const uint8_t *)"", 0,
+    8, 1, 8000, 0, ENDIAN_LITTLE, (const uint8_t *)"", 0
+  },
+  {
+    "sign flip",
+    8, 1, 8000, 0, ENDIAN_LITTLE, simple_bytes_u, 4,
+    8, 1, 8000, 1, ENDIAN_LITTLE, simple_bytes_s, 4
+  },
+#if HAVE_SAMPLERATE_H
+  /* Conversions that do change the sample rate */
+  
+#endif
+};
+#define NCONVERSIONS (sizeof conversions / sizeof *conversions)
+
+static void test_resample(void) {
+  for(size_t n = 0; n < NCONVERSIONS; ++n) {
+    struct resampler rs[1];
+
+    resample_init(rs, 
+                  conversions[n].input_bits,
+                  conversions[n].input_channels,
+                  conversions[n].input_rate,
+                  conversions[n].input_signed,
+                  conversions[n].input_endian,
+                  conversions[n].output_bits,
+                  conversions[n].output_channels,
+                  conversions[n].output_rate,
+                  conversions[n].output_signed,
+                  conversions[n].output_endian);
+    size_t output_bytes;
+    const uint8_t *output = convert(rs,
+                                    conversions[n].input, 
+                                    conversions[n].input_bytes, 
+                                    &output_bytes);
+    if(output_bytes != conversions[n].output_bytes
+       || memcmp(output, conversions[n].output, output_bytes)) {
+      fprintf(stderr, "index %zu description %s mismatch\n",
+              n, conversions[n].description);
+      size_t k = 0;
+      while(k < conversions[n].output_bytes || k < output_bytes) {
+        size_t j = 0;
+        fprintf(stderr, "%8zu E:", k);
+        for(j = 0; j < 16; ++j) {
+          if(j + k < conversions[n].output_bytes)
+            fprintf(stderr, " %02x", conversions[n].output[j + k]);
+        }
+        fprintf(stderr, "\n         G:");
+        for(j = 0; j < 16; ++j) {
+          if(j + k < output_bytes)
+            fprintf(stderr, " %02x", output[j + k]);
+        }
+        fprintf(stderr, "\n");
+        k += 16;
+      }
+      ++errors;
+    }
+    ++tests;
+  }
+}
+
+TEST(resample);
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/