Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.xiph.org/xiph/opus.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAndrew Allen <bitllama@google.com>2017-11-08 00:26:23 +0300
committerJean-Marc Valin <jmvalin@jmvalin.ca>2017-11-08 01:44:36 +0300
commitf643c03fba45ad3547b0e54cc6c0f570361002bf (patch)
tree38e6605bf9b39151299c364731ec7dde3c92aefc /tests
parente12df85a62598c7acbee84f13602f82eb6d39734 (diff)
Support for Channel Mapping 253
OpusProjection* classes MixingMatrix class Projection tests Change-Id: I98644466abf4ffd36e48bdecad1204d69e1539b9 Signed-off-by: Jean-Marc Valin <jmvalin@jmvalin.ca>
Diffstat (limited to 'tests')
-rw-r--r--tests/test_opus_projection.c431
1 files changed, 431 insertions, 0 deletions
diff --git a/tests/test_opus_projection.c b/tests/test_opus_projection.c
new file mode 100644
index 00000000..4b11c129
--- /dev/null
+++ b/tests/test_opus_projection.c
@@ -0,0 +1,431 @@
+/* Copyright (c) 2017 Google Inc.
+ Written by Andrew Allen */
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include "float_cast.h"
+#include "opus.h"
+#include "test_opus_common.h"
+#include "opus_projection.h"
+#include "mathops.h"
+#include "../src/mapping_matrix.c"
+#include "mathops.c"
+
+#ifdef ENABLE_EXPERIMENTAL_AMBISONICS
+
+#define BUFFER_SIZE 960
+#define MAX_DATA_BYTES 32768
+#define MAX_FRAME_SAMPLES 5760
+
+#define INT16_TO_FLOAT(x) ((1/32768.f)*(float)x)
+
+void print_matrix_short(const opus_int16 *data, int rows, int cols)
+{
+ int i, j;
+ for (i = 0; i < rows; i++)
+ {
+ for (j = 0; j < cols; j++)
+ {
+ fprintf(stderr, "%8.5f ", (float)INT16_TO_FLOAT(data[j * rows + i]));
+ }
+ fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "\n");
+}
+
+void print_matrix_float(const float *data, int rows, int cols)
+{
+ int i, j;
+ for (i = 0; i < rows; i++)
+ {
+ for (j = 0; j < cols; j++)
+ {
+ fprintf(stderr, "%8.5f ", data[j * rows + i]);
+ }
+ fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "\n");
+}
+
+void print_matrix(MappingMatrix *matrix)
+{
+ opus_int16 *data;
+
+ fprintf(stderr, "%d x %d, gain: %d\n", matrix->rows, matrix->cols,
+ matrix->gain);
+
+ data = mapping_matrix_get_data(matrix);
+ print_matrix_short(data, matrix->rows, matrix->cols);
+}
+
+int assert_transform_short(
+ const opus_int16 *a, const opus_int16 *b, int size, opus_int16 tolerance)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (abs(a[i] - b[i]) > tolerance)
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int assert_transform_float(
+ const float *a, const float *b, int size, float tolerance)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (fabsf(a[i] - b[i]) > tolerance)
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void test_matrix_transform(void)
+{
+ /* Create testing mixing matrix (4 x 3), gain 0dB:
+ * [ 0 1 0 ]
+ * [ 1 0 0 ]
+ * [ 0 0 0 ]
+ * [ 0 0 1 ]
+ */
+ opus_int32 matrix_size;
+ MappingMatrix *testing_matrix;
+ const opus_int16 testing_matrix_data[12] = {
+ 0, 32767, 0, 0, 32767, 0, 0, 0, 0, 0, 0, 32767 };
+
+ const int frame_size = 10;
+ const opus_int16 input[30] = {
+ 32767, 0, -32768, 29491, -3277, -29491, 26214, -6554, -26214, 22938, -9830,
+ -22938, 19661, -13107, -19661, 16384, -16384, -16384, 13107, -19661, -13107,
+ 9830, -22938, -9830, 6554, -26214, -6554, 3277, -29491, -3277};
+ const opus_int16 expected_output[40] = {
+ 0, 32767, 0, -32768, -3277, 29491, 0, -29491, -6554, 26214, 0, -26214,
+ -9830, 22938, 0, -22938, -13107, 19661, 0, -19661, -16384, 16384, 0, -16384,
+ -19661, 13107, 0, -13107, -22938, 9830, 0, -9830, -26214, 6554, 0, -6554,
+ -29491, 3277, 0, -3277};
+ opus_int16 output[40] = {0};
+
+#ifndef DISABLE_FLOAT_API
+ int i;
+ /* Sample-accurate to -93.9794 dB */
+ float flt_tolerance = 2e-5f;
+ float input32[30] = {0};
+ float output32[40] = {0};
+ float expected_output32[40] = {0};
+
+ /* Convert short to float representations. */
+ for (i = 0; i < 30; i++)
+ {
+ input32[i] = INT16_TO_FLOAT(input[i]);
+ }
+ for (i = 0; i < 40; i++)
+ {
+ expected_output32[i] = INT16_TO_FLOAT(expected_output[i]);
+ }
+#endif /* DISABLE_FLOAT_API */
+
+ /* Create the matrix. */
+ matrix_size = mapping_matrix_get_size(4, 3);
+ testing_matrix = (MappingMatrix *)opus_alloc(matrix_size);
+ mapping_matrix_init(testing_matrix, 4, 3, 0, testing_matrix_data,
+ 12 * sizeof(opus_int16));
+
+ mapping_matrix_multiply_short(testing_matrix, input, testing_matrix->cols,
+ output, testing_matrix->rows, frame_size);
+ if (!assert_transform_short(output, expected_output, 40, 1))
+ {
+ fprintf(stderr, "Matrix:\n");
+ print_matrix(testing_matrix);
+
+ fprintf(stderr, "Input (short):\n");
+ print_matrix_short(input, testing_matrix->cols, frame_size);
+
+ fprintf(stderr, "Expected Output (short):\n");
+ print_matrix_short(expected_output, testing_matrix->rows, frame_size);
+
+ fprintf(stderr, "Output (short):\n");
+ print_matrix_short(output, testing_matrix->rows, frame_size);
+
+ goto bad_cleanup;
+ }
+
+#ifndef DISABLE_FLOAT_API
+ mapping_matrix_multiply_float(testing_matrix, input32, testing_matrix->cols,
+ output32, testing_matrix->rows, frame_size);
+ if (!assert_transform_float(output32, expected_output32, 40, flt_tolerance))
+ {
+ fprintf(stderr, "Matrix:\n");
+ print_matrix(testing_matrix);
+
+ fprintf(stderr, "Input (float):\n");
+ print_matrix_float(input32, testing_matrix->cols, frame_size);
+
+ fprintf(stderr, "Expected Output (float):\n");
+ print_matrix_float(expected_output32, testing_matrix->rows, frame_size);
+
+ fprintf(stderr, "Output (float):\n");
+ print_matrix_float(output32, testing_matrix->rows, frame_size);
+
+ goto bad_cleanup;
+ }
+#endif
+ opus_free(testing_matrix);
+ return;
+bad_cleanup:
+ opus_free(testing_matrix);
+ test_failed();
+}
+
+void test_creation_arguments(const int channels, const int mapping_family)
+{
+ int streams;
+ int coupled_streams;
+ int enc_error;
+ int dec_error;
+ int ret;
+ OpusProjectionEncoder *st_enc = NULL;
+ OpusProjectionDecoder *st_dec = NULL;
+
+ const opus_int32 Fs = 48000;
+ const int application = OPUS_APPLICATION_AUDIO;
+
+ int order_plus_one = (int)floor(sqrt((float)channels));
+ int nondiegetic_channels = channels - order_plus_one * order_plus_one;
+
+ int is_channels_valid = 0;
+ int is_projection_valid = 0;
+
+ st_enc = opus_projection_ambisonics_encoder_create(Fs, channels,
+ mapping_family, &streams, &coupled_streams, application, &enc_error);
+ if (st_enc != NULL)
+ {
+ opus_int32 matrix_size;
+ unsigned char *matrix;
+
+ ret = opus_projection_encoder_ctl(st_enc,
+ OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size);
+ if (ret != OPUS_OK || !matrix_size)
+ test_failed();
+
+ matrix = (unsigned char *)opus_alloc(matrix_size);
+ ret = opus_projection_encoder_ctl(st_enc,
+ OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size);
+
+ opus_projection_encoder_destroy(st_enc);
+
+ st_dec = opus_projection_decoder_create(Fs, channels, streams,
+ coupled_streams, matrix, matrix_size, &dec_error);
+ if (st_dec != NULL)
+ {
+ opus_projection_decoder_destroy(st_dec);
+ }
+ opus_free(matrix);
+ }
+
+ is_channels_valid = (order_plus_one >= 2 && order_plus_one <= 4) &&
+ (nondiegetic_channels == 0 || nondiegetic_channels == 2);
+ is_projection_valid = (enc_error == OPUS_OK && dec_error == OPUS_OK);
+ if (is_channels_valid ^ is_projection_valid)
+ {
+ fprintf(stderr, "Channels: %d, Family: %d\n", channels, mapping_family);
+ fprintf(stderr, "Order+1: %d, Non-diegetic Channels: %d\n",
+ order_plus_one, nondiegetic_channels);
+ fprintf(stderr, "Streams: %d, Coupled Streams: %d\n",
+ streams, coupled_streams);
+ test_failed();
+ }
+}
+
+void generate_music(short *buf, opus_int32 len, opus_int32 channels)
+{
+ opus_int32 i,j,k;
+ opus_int32 *a,*b,*c,*d;
+ a = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
+ b = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
+ c = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
+ d = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
+ memset(a, 0, sizeof(opus_int32) * channels);
+ memset(b, 0, sizeof(opus_int32) * channels);
+ memset(c, 0, sizeof(opus_int32) * channels);
+ memset(d, 0, sizeof(opus_int32) * channels);
+ j=0;
+
+ for(i=0;i<len;i++)
+ {
+ for(k=0;k<channels;k++)
+ {
+ opus_uint32 r;
+ opus_int32 v;
+ v=(((j*((j>>12)^((j>>10|j>>12)&26&j>>7)))&128)+128)<<15;
+ r=fast_rand();v+=r&65535;v-=r>>16;
+ b[k]=v-a[k]+((b[k]*61+32)>>6);a[k]=v;
+ c[k]=(30*(c[k]+b[k]+d[k])+32)>>6;d[k]=b[k];
+ v=(c[k]+128)>>8;
+ buf[i*channels+k]=v>32767?32767:(v<-32768?-32768:v);
+ if(i%6==0)j++;
+ }
+ }
+
+ free(a);
+ free(b);
+ free(c);
+ free(d);
+}
+
+void test_encode_decode(opus_int32 bitrate, opus_int32 channels,
+ const int mapping_family)
+{
+ const opus_int32 Fs = 48000;
+ const int application = OPUS_APPLICATION_AUDIO;
+
+ OpusProjectionEncoder *st_enc;
+ OpusProjectionDecoder *st_dec;
+ int streams;
+ int coupled;
+ int error;
+ short *buffer_in;
+ short *buffer_out;
+ unsigned char data[MAX_DATA_BYTES] = { 0 };
+ int len;
+ int out_samples;
+ opus_int32 matrix_size = 0;
+ unsigned char *matrix = NULL;
+
+ buffer_in = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels);
+ buffer_out = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels);
+
+ st_enc = opus_projection_ambisonics_encoder_create(Fs, channels,
+ mapping_family, &streams, &coupled, application, &error);
+ if (error != OPUS_OK) {
+ fprintf(stderr,
+ "Couldn\'t create encoder with %d channels and mapping family %d.\n",
+ channels, mapping_family);
+ free(buffer_in);
+ free(buffer_out);
+ test_failed();
+ }
+
+ error = opus_projection_encoder_ctl(st_enc,
+ OPUS_SET_BITRATE(bitrate * 1000 * (streams + coupled)));
+ if (error != OPUS_OK)
+ {
+ goto bad_cleanup;
+ }
+
+ error = opus_projection_encoder_ctl(st_enc,
+ OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size);
+ if (error != OPUS_OK || !matrix_size)
+ {
+ goto bad_cleanup;
+ }
+
+ matrix = (unsigned char *)opus_alloc(matrix_size);
+ error = opus_projection_encoder_ctl(st_enc,
+ OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size);
+
+ st_dec = opus_projection_decoder_create(Fs, channels, streams, coupled,
+ matrix, matrix_size, &error);
+ opus_free(matrix);
+
+ if (error != OPUS_OK) {
+ fprintf(stderr,
+ "Couldn\'t create decoder with %d channels, %d streams "
+ "and %d coupled streams.\n", channels, streams, coupled);
+ goto bad_cleanup;
+ }
+
+ generate_music(buffer_in, BUFFER_SIZE, channels);
+
+ len = opus_projection_encode(
+ st_enc, buffer_in, BUFFER_SIZE, data, MAX_DATA_BYTES);
+ if(len<0 || len>MAX_DATA_BYTES) {
+ fprintf(stderr,"opus_encode() returned %d\n", len);
+ goto bad_cleanup;
+ }
+
+ out_samples = opus_projection_decode(
+ st_dec, data, len, buffer_out, MAX_FRAME_SAMPLES, 0);
+ if(out_samples!=BUFFER_SIZE) {
+ fprintf(stderr,"opus_decode() returned %d\n", out_samples);
+ goto bad_cleanup;
+ }
+
+ free(buffer_in);
+ free(buffer_out);
+ return;
+bad_cleanup:
+ free(buffer_in);
+ free(buffer_out);
+ test_failed();
+}
+
+int main(int _argc, char **_argv)
+{
+ unsigned int i;
+
+ (void)_argc;
+ (void)_argv;
+
+ /* Test matrix creation/multiplication. */
+ test_matrix_transform();
+
+ /* Test full range of channels in creation arguments. */
+ for (i = 0; i < 255; i++)
+ test_creation_arguments(i, 253);
+
+ /* Test encode/decode pipeline. */
+ test_encode_decode(64 * 16, 16, 253);
+
+ fprintf(stderr, "All projection tests passed.\n");
+ return 0;
+}
+
+#else
+
+int main(int _argc, char **_argv)
+{
+ (void)_argc;
+ (void)_argv;
+ fprintf(stderr, "Projection tests are disabled. "
+ "Configure with --enable-ambisonics for support.\n");
+ return 0;
+}
+
+#endif /* ENABLE_EXPERIMENTAL_AMBISONICS */