cannam@154: /* Copyright (c) 2017 Google Inc. cannam@154: Written by Andrew Allen */ cannam@154: /* cannam@154: Redistribution and use in source and binary forms, with or without cannam@154: modification, are permitted provided that the following conditions cannam@154: are met: cannam@154: cannam@154: - Redistributions of source code must retain the above copyright cannam@154: notice, this list of conditions and the following disclaimer. cannam@154: cannam@154: - Redistributions in binary form must reproduce the above copyright cannam@154: notice, this list of conditions and the following disclaimer in the cannam@154: documentation and/or other materials provided with the distribution. cannam@154: cannam@154: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS cannam@154: ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT cannam@154: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR cannam@154: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER cannam@154: OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, cannam@154: EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, cannam@154: PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR cannam@154: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF cannam@154: LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING cannam@154: NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS cannam@154: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cannam@154: */ cannam@154: cannam@154: #ifdef HAVE_CONFIG_H cannam@154: #include "config.h" cannam@154: #endif cannam@154: cannam@154: #include cannam@154: #include cannam@154: #include cannam@154: #include cannam@154: #include cannam@154: #include "float_cast.h" cannam@154: #include "opus.h" cannam@154: #include "test_opus_common.h" cannam@154: #include "opus_projection.h" cannam@154: #include "mathops.h" cannam@154: #include "../src/mapping_matrix.h" cannam@154: #include "mathops.h" cannam@154: cannam@154: #define BUFFER_SIZE 960 cannam@154: #define MAX_DATA_BYTES 32768 cannam@154: #define MAX_FRAME_SAMPLES 5760 cannam@154: #define ERROR_TOLERANCE 1 cannam@154: cannam@154: #define SIMPLE_MATRIX_SIZE 12 cannam@154: #define SIMPLE_MATRIX_FRAME_SIZE 10 cannam@154: #define SIMPLE_MATRIX_INPUT_SIZE 30 cannam@154: #define SIMPLE_MATRIX_OUTPUT_SIZE 40 cannam@154: cannam@154: int assert_is_equal( cannam@154: const opus_val16 *a, const opus_int16 *b, int size, opus_int16 tolerance) cannam@154: { cannam@154: int i; cannam@154: for (i = 0; i < size; i++) cannam@154: { cannam@154: #ifdef FIXED_POINT cannam@154: opus_int16 val = a[i]; cannam@154: #else cannam@154: opus_int16 val = FLOAT2INT16(a[i]); cannam@154: #endif cannam@154: if (abs(val - b[i]) > tolerance) cannam@154: return 1; cannam@154: } cannam@154: return 0; cannam@154: } cannam@154: cannam@154: int assert_is_equal_short( cannam@154: const opus_int16 *a, const opus_int16 *b, int size, opus_int16 tolerance) cannam@154: { cannam@154: int i; cannam@154: for (i = 0; i < size; i++) cannam@154: if (abs(a[i] - b[i]) > tolerance) cannam@154: return 1; cannam@154: return 0; cannam@154: } cannam@154: cannam@154: void test_simple_matrix(void) cannam@154: { cannam@154: const MappingMatrix simple_matrix_params = {4, 3, 0}; cannam@154: const opus_int16 simple_matrix_data[SIMPLE_MATRIX_SIZE] = {0, 32767, 0, 0, 32767, 0, 0, 0, 0, 0, 0, 32767}; cannam@154: const opus_int16 input_int16[SIMPLE_MATRIX_INPUT_SIZE] = { cannam@154: 32767, 0, -32768, 29491, -3277, -29491, 26214, -6554, -26214, 22938, -9830, cannam@154: -22938, 19661, -13107, -19661, 16384, -16384, -16384, 13107, -19661, -13107, cannam@154: 9830, -22938, -9830, 6554, -26214, -6554, 3277, -29491, -3277}; cannam@154: const opus_int16 expected_output_int16[SIMPLE_MATRIX_OUTPUT_SIZE] = { cannam@154: 0, 32767, 0, -32768, -3277, 29491, 0, -29491, -6554, 26214, 0, -26214, cannam@154: -9830, 22938, 0, -22938, -13107, 19661, 0, -19661, -16384, 16384, 0, -16384, cannam@154: -19661, 13107, 0, -13107, -22938, 9830, 0, -9830, -26214, 6554, 0, -6554, cannam@154: -29491, 3277, 0, -3277}; cannam@154: cannam@154: int i, ret; cannam@154: opus_int32 simple_matrix_size; cannam@154: opus_val16 *input_val16; cannam@154: opus_val16 *output_val16; cannam@154: opus_int16 *output_int16; cannam@154: MappingMatrix *simple_matrix; cannam@154: cannam@154: /* Allocate input/output buffers. */ cannam@154: input_val16 = (opus_val16 *)opus_alloc(sizeof(opus_val16) * SIMPLE_MATRIX_INPUT_SIZE); cannam@154: output_int16 = (opus_int16 *)opus_alloc(sizeof(opus_int16) * SIMPLE_MATRIX_OUTPUT_SIZE); cannam@154: output_val16 = (opus_val16 *)opus_alloc(sizeof(opus_val16) * SIMPLE_MATRIX_OUTPUT_SIZE); cannam@154: cannam@154: /* Initialize matrix */ cannam@154: simple_matrix_size = mapping_matrix_get_size(simple_matrix_params.rows, cannam@154: simple_matrix_params.cols); cannam@154: if (!simple_matrix_size) cannam@154: test_failed(); cannam@154: cannam@154: simple_matrix = (MappingMatrix *)opus_alloc(simple_matrix_size); cannam@154: mapping_matrix_init(simple_matrix, simple_matrix_params.rows, cannam@154: simple_matrix_params.cols, simple_matrix_params.gain, simple_matrix_data, cannam@154: sizeof(simple_matrix_data)); cannam@154: cannam@154: /* Copy inputs. */ cannam@154: for (i = 0; i < SIMPLE_MATRIX_INPUT_SIZE; i++) cannam@154: { cannam@154: #ifdef FIXED_POINT cannam@154: input_val16[i] = input_int16[i]; cannam@154: #else cannam@154: input_val16[i] = (1/32768.f)*input_int16[i]; cannam@154: #endif cannam@154: } cannam@154: cannam@154: /* _in_short */ cannam@154: for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++) cannam@154: output_val16[i] = 0; cannam@154: for (i = 0; i < simple_matrix->rows; i++) cannam@154: { cannam@154: mapping_matrix_multiply_channel_in_short(simple_matrix, cannam@154: input_int16, simple_matrix->cols, &output_val16[i], i, cannam@154: simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE); cannam@154: } cannam@154: ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE); cannam@154: if (ret) cannam@154: test_failed(); cannam@154: cannam@154: /* _out_short */ cannam@154: for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++) cannam@154: output_int16[i] = 0; cannam@154: for (i = 0; i < simple_matrix->cols; i++) cannam@154: { cannam@154: mapping_matrix_multiply_channel_out_short(simple_matrix, cannam@154: &input_val16[i], i, simple_matrix->cols, output_int16, cannam@154: simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE); cannam@154: } cannam@154: ret = assert_is_equal_short(output_int16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE); cannam@154: if (ret) cannam@154: test_failed(); cannam@154: cannam@154: #if !defined(DISABLE_FLOAT_API) && !defined(FIXED_POINT) cannam@154: /* _in_float */ cannam@154: for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++) cannam@154: output_val16[i] = 0; cannam@154: for (i = 0; i < simple_matrix->rows; i++) cannam@154: { cannam@154: mapping_matrix_multiply_channel_in_float(simple_matrix, cannam@154: input_val16, simple_matrix->cols, &output_val16[i], i, cannam@154: simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE); cannam@154: } cannam@154: ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE); cannam@154: if (ret) cannam@154: test_failed(); cannam@154: cannam@154: /* _out_float */ cannam@154: for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++) cannam@154: output_val16[i] = 0; cannam@154: for (i = 0; i < simple_matrix->cols; i++) cannam@154: { cannam@154: mapping_matrix_multiply_channel_out_float(simple_matrix, cannam@154: &input_val16[i], i, simple_matrix->cols, output_val16, cannam@154: simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE); cannam@154: } cannam@154: ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE); cannam@154: if (ret) cannam@154: test_failed(); cannam@154: #endif cannam@154: cannam@154: opus_free(input_val16); cannam@154: opus_free(output_int16); cannam@154: opus_free(output_val16); cannam@154: opus_free(simple_matrix); cannam@154: } cannam@154: cannam@154: void test_creation_arguments(const int channels, const int mapping_family) cannam@154: { cannam@154: int streams; cannam@154: int coupled_streams; cannam@154: int enc_error; cannam@154: int dec_error; cannam@154: int ret; cannam@154: OpusProjectionEncoder *st_enc = NULL; cannam@154: OpusProjectionDecoder *st_dec = NULL; cannam@154: cannam@154: const opus_int32 Fs = 48000; cannam@154: const int application = OPUS_APPLICATION_AUDIO; cannam@154: cannam@154: int order_plus_one = (int)floor(sqrt((float)channels)); cannam@154: int nondiegetic_channels = channels - order_plus_one * order_plus_one; cannam@154: cannam@154: int is_channels_valid = 0; cannam@154: int is_projection_valid = 0; cannam@154: cannam@154: st_enc = opus_projection_ambisonics_encoder_create(Fs, channels, cannam@154: mapping_family, &streams, &coupled_streams, application, &enc_error); cannam@154: if (st_enc != NULL) cannam@154: { cannam@154: opus_int32 matrix_size; cannam@154: unsigned char *matrix; cannam@154: cannam@154: ret = opus_projection_encoder_ctl(st_enc, cannam@154: OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size); cannam@154: if (ret != OPUS_OK || !matrix_size) cannam@154: test_failed(); cannam@154: cannam@154: matrix = (unsigned char *)opus_alloc(matrix_size); cannam@154: ret = opus_projection_encoder_ctl(st_enc, cannam@154: OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size); cannam@154: cannam@154: opus_projection_encoder_destroy(st_enc); cannam@154: cannam@154: st_dec = opus_projection_decoder_create(Fs, channels, streams, cannam@154: coupled_streams, matrix, matrix_size, &dec_error); cannam@154: if (st_dec != NULL) cannam@154: { cannam@154: opus_projection_decoder_destroy(st_dec); cannam@154: } cannam@154: opus_free(matrix); cannam@154: } cannam@154: cannam@154: is_channels_valid = (order_plus_one >= 2 && order_plus_one <= 4) && cannam@154: (nondiegetic_channels == 0 || nondiegetic_channels == 2); cannam@154: is_projection_valid = (enc_error == OPUS_OK && dec_error == OPUS_OK); cannam@154: if (is_channels_valid ^ is_projection_valid) cannam@154: { cannam@154: fprintf(stderr, "Channels: %d, Family: %d\n", channels, mapping_family); cannam@154: fprintf(stderr, "Order+1: %d, Non-diegetic Channels: %d\n", cannam@154: order_plus_one, nondiegetic_channels); cannam@154: fprintf(stderr, "Streams: %d, Coupled Streams: %d\n", cannam@154: streams, coupled_streams); cannam@154: test_failed(); cannam@154: } cannam@154: } cannam@154: cannam@154: void generate_music(short *buf, opus_int32 len, opus_int32 channels) cannam@154: { cannam@154: opus_int32 i,j,k; cannam@154: opus_int32 *a,*b,*c,*d; cannam@154: a = (opus_int32 *)malloc(sizeof(opus_int32) * channels); cannam@154: b = (opus_int32 *)malloc(sizeof(opus_int32) * channels); cannam@154: c = (opus_int32 *)malloc(sizeof(opus_int32) * channels); cannam@154: d = (opus_int32 *)malloc(sizeof(opus_int32) * channels); cannam@154: memset(a, 0, sizeof(opus_int32) * channels); cannam@154: memset(b, 0, sizeof(opus_int32) * channels); cannam@154: memset(c, 0, sizeof(opus_int32) * channels); cannam@154: memset(d, 0, sizeof(opus_int32) * channels); cannam@154: j=0; cannam@154: cannam@154: for(i=0;i>12)^((j>>10|j>>12)&26&j>>7)))&128)+128)<<15; cannam@154: r=fast_rand();v+=r&65535;v-=r>>16; cannam@154: b[k]=v-a[k]+((b[k]*61+32)>>6);a[k]=v; cannam@154: c[k]=(30*(c[k]+b[k]+d[k])+32)>>6;d[k]=b[k]; cannam@154: v=(c[k]+128)>>8; cannam@154: buf[i*channels+k]=v>32767?32767:(v<-32768?-32768:v); cannam@154: if(i%6==0)j++; cannam@154: } cannam@154: } cannam@154: cannam@154: free(a); cannam@154: free(b); cannam@154: free(c); cannam@154: free(d); cannam@154: } cannam@154: cannam@154: void test_encode_decode(opus_int32 bitrate, opus_int32 channels, cannam@154: const int mapping_family) cannam@154: { cannam@154: const opus_int32 Fs = 48000; cannam@154: const int application = OPUS_APPLICATION_AUDIO; cannam@154: cannam@154: OpusProjectionEncoder *st_enc; cannam@154: OpusProjectionDecoder *st_dec; cannam@154: int streams; cannam@154: int coupled; cannam@154: int error; cannam@154: short *buffer_in; cannam@154: short *buffer_out; cannam@154: unsigned char data[MAX_DATA_BYTES] = { 0 }; cannam@154: int len; cannam@154: int out_samples; cannam@154: opus_int32 matrix_size = 0; cannam@154: unsigned char *matrix = NULL; cannam@154: cannam@154: buffer_in = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels); cannam@154: buffer_out = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels); cannam@154: cannam@154: st_enc = opus_projection_ambisonics_encoder_create(Fs, channels, cannam@154: mapping_family, &streams, &coupled, application, &error); cannam@154: if (error != OPUS_OK) { cannam@154: fprintf(stderr, cannam@154: "Couldn\'t create encoder with %d channels and mapping family %d.\n", cannam@154: channels, mapping_family); cannam@154: free(buffer_in); cannam@154: free(buffer_out); cannam@154: test_failed(); cannam@154: } cannam@154: cannam@154: error = opus_projection_encoder_ctl(st_enc, cannam@154: OPUS_SET_BITRATE(bitrate * 1000 * (streams + coupled))); cannam@154: if (error != OPUS_OK) cannam@154: { cannam@154: goto bad_cleanup; cannam@154: } cannam@154: cannam@154: error = opus_projection_encoder_ctl(st_enc, cannam@154: OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size); cannam@154: if (error != OPUS_OK || !matrix_size) cannam@154: { cannam@154: goto bad_cleanup; cannam@154: } cannam@154: cannam@154: matrix = (unsigned char *)opus_alloc(matrix_size); cannam@154: error = opus_projection_encoder_ctl(st_enc, cannam@154: OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size); cannam@154: cannam@154: st_dec = opus_projection_decoder_create(Fs, channels, streams, coupled, cannam@154: matrix, matrix_size, &error); cannam@154: opus_free(matrix); cannam@154: cannam@154: if (error != OPUS_OK) { cannam@154: fprintf(stderr, cannam@154: "Couldn\'t create decoder with %d channels, %d streams " cannam@154: "and %d coupled streams.\n", channels, streams, coupled); cannam@154: goto bad_cleanup; cannam@154: } cannam@154: cannam@154: generate_music(buffer_in, BUFFER_SIZE, channels); cannam@154: cannam@154: len = opus_projection_encode( cannam@154: st_enc, buffer_in, BUFFER_SIZE, data, MAX_DATA_BYTES); cannam@154: if(len<0 || len>MAX_DATA_BYTES) { cannam@154: fprintf(stderr,"opus_encode() returned %d\n", len); cannam@154: goto bad_cleanup; cannam@154: } cannam@154: cannam@154: out_samples = opus_projection_decode( cannam@154: st_dec, data, len, buffer_out, MAX_FRAME_SAMPLES, 0); cannam@154: if(out_samples!=BUFFER_SIZE) { cannam@154: fprintf(stderr,"opus_decode() returned %d\n", out_samples); cannam@154: goto bad_cleanup; cannam@154: } cannam@154: cannam@154: free(buffer_in); cannam@154: free(buffer_out); cannam@154: return; cannam@154: bad_cleanup: cannam@154: free(buffer_in); cannam@154: free(buffer_out); cannam@154: test_failed(); cannam@154: } cannam@154: cannam@154: int main(int _argc, char **_argv) cannam@154: { cannam@154: unsigned int i; cannam@154: cannam@154: (void)_argc; cannam@154: (void)_argv; cannam@154: cannam@154: /* Test simple matrix multiplication routines. */ cannam@154: test_simple_matrix(); cannam@154: cannam@154: /* Test full range of channels in creation arguments. */ cannam@154: for (i = 0; i < 255; i++) cannam@154: test_creation_arguments(i, 3); cannam@154: cannam@154: /* Test encode/decode pipeline. */ cannam@154: test_encode_decode(64 * 18, 18, 3); cannam@154: cannam@154: fprintf(stderr, "All projection tests passed.\n"); cannam@154: return 0; cannam@154: } cannam@154: