Support for Channel Mapping 253
OpusProjection* classes MixingMatrix class Projection tests Change-Id: I98644466abf4ffd36e48bdecad1204d69e1539b9 Signed-off-by: Jean-Marc Valin <jmvalin@jmvalin.ca>
This commit is contained in:
parent
e12df85a62
commit
f643c03fba
12 changed files with 2116 additions and 48 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -49,6 +49,7 @@ tests/test_opus_api
|
|||
tests/test_opus_decode
|
||||
tests/test_opus_encode
|
||||
tests/test_opus_padding
|
||||
tests/test_opus_projection
|
||||
celt/arm/armopts.s
|
||||
celt/dump_modes/dump_modes
|
||||
celt/tests/test_unit_cwrs32
|
||||
|
|
11
Makefile.am
11
Makefile.am
|
@ -82,7 +82,7 @@ if OPUS_ARM_EXTERNAL_ASM
|
|||
libopus_la_LIBADD += libarmasm.la
|
||||
endif
|
||||
|
||||
pkginclude_HEADERS = include/opus.h include/opus_multistream.h include/opus_types.h include/opus_defines.h
|
||||
pkginclude_HEADERS = include/opus.h include/opus_multistream.h include/opus_types.h include/opus_defines.h include/opus_projection.h
|
||||
|
||||
noinst_HEADERS = $(OPUS_HEAD) $(SILK_HEAD) $(CELT_HEAD)
|
||||
|
||||
|
@ -102,7 +102,8 @@ noinst_PROGRAMS = celt/tests/test_unit_cwrs32 \
|
|||
tests/test_opus_api \
|
||||
tests/test_opus_decode \
|
||||
tests/test_opus_encode \
|
||||
tests/test_opus_padding
|
||||
tests/test_opus_padding \
|
||||
tests/test_opus_projection
|
||||
|
||||
TESTS = celt/tests/test_unit_cwrs32 \
|
||||
celt/tests/test_unit_dft \
|
||||
|
@ -116,7 +117,8 @@ TESTS = celt/tests/test_unit_cwrs32 \
|
|||
tests/test_opus_api \
|
||||
tests/test_opus_decode \
|
||||
tests/test_opus_encode \
|
||||
tests/test_opus_padding
|
||||
tests/test_opus_padding \
|
||||
tests/test_opus_projection
|
||||
|
||||
opus_demo_SOURCES = src/opus_demo.c
|
||||
|
||||
|
@ -141,6 +143,9 @@ tests_test_opus_decode_LDADD = libopus.la $(NE10_LIBS) $(LIBM)
|
|||
tests_test_opus_padding_SOURCES = tests/test_opus_padding.c tests/test_opus_common.h
|
||||
tests_test_opus_padding_LDADD = libopus.la $(NE10_LIBS) $(LIBM)
|
||||
|
||||
tests_test_opus_projection_SOURCES = tests/test_opus_projection.c tests/test_opus_common.h
|
||||
tests_test_opus_projection_LDADD = libopus.la $(NE10_LIBS) $(LIBM)
|
||||
|
||||
CELT_OBJ = $(CELT_SOURCES:.c=.lo)
|
||||
SILK_OBJ = $(SILK_SOURCES:.c=.lo)
|
||||
|
||||
|
|
568
include/opus_projection.h
Normal file
568
include/opus_projection.h
Normal file
|
@ -0,0 +1,568 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file opus_projection.h
|
||||
* @brief Opus projection reference API
|
||||
*/
|
||||
|
||||
#ifndef OPUS_PROJECTION_H
|
||||
#define OPUS_PROJECTION_H
|
||||
|
||||
#include "opus_multistream.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @cond OPUS_INTERNAL_DOC */
|
||||
|
||||
/** These are the actual encoder and decoder CTL ID numbers.
|
||||
* They should not be used directly by applications.c
|
||||
* In general, SETs should be even and GETs should be odd.*/
|
||||
/**@{*/
|
||||
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN_REQUEST 6001
|
||||
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST 6003
|
||||
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST 6005
|
||||
/**@}*/
|
||||
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/** @defgroup opus_projection_ctls Projection specific encoder and decoder CTLs
|
||||
*
|
||||
* These are convenience macros that are specific to the
|
||||
* opus_projection_encoder_ctl() and opus_projection_decoder_ctl()
|
||||
* interface.
|
||||
* The CTLs from @ref opus_genericctls, @ref opus_encoderctls,
|
||||
* @ref opus_decoderctls, and @ref opus_multistream_ctls may be applied to a
|
||||
* projection encoder or decoder as well.
|
||||
*/
|
||||
/**@{*/
|
||||
|
||||
/** Gets the gain (in dB. S7.8-format) of the demixing matrix from the encoder.
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns the gain (in dB. S7.8-format)
|
||||
* of the demixing matrix.
|
||||
* @hideinitializer
|
||||
*/
|
||||
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN(x) OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
|
||||
/** Gets the size in bytes of the demixing matrix from the encoder.
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns the size in bytes of the
|
||||
* demixing matrix.
|
||||
* @hideinitializer
|
||||
*/
|
||||
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE(x) OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
|
||||
/** Copies the demixing matrix to the supplied pointer location.
|
||||
* @param[out] x <tt>unsigned char *</tt>: Returns the demixing matrix to the
|
||||
* supplied pointer location.
|
||||
* @param y <tt>opus_in32</tt>: The size in bytes of the reserved memory at the
|
||||
* pointer location.
|
||||
* @hideinitializer
|
||||
*/
|
||||
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX(x,y) OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, x, __opus_check_int(y)
|
||||
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** Opus projection encoder state.
|
||||
* This contains the complete state of a projection Opus encoder.
|
||||
* It is position independent and can be freely copied.
|
||||
* @see opus_projection_ambisonics_encoder_create
|
||||
*/
|
||||
typedef struct OpusProjectionEncoder OpusProjectionEncoder;
|
||||
|
||||
|
||||
/** Opus projection decoder state.
|
||||
* This contains the complete state of a projection Opus decoder.
|
||||
* It is position independent and can be freely copied.
|
||||
* @see opus_projection_decoder_create
|
||||
* @see opus_projection_decoder_init
|
||||
*/
|
||||
typedef struct OpusProjectionDecoder OpusProjectionDecoder;
|
||||
|
||||
|
||||
/**\name Projection encoder functions */
|
||||
/**@{*/
|
||||
|
||||
/** Gets the size of an OpusProjectionEncoder structure.
|
||||
* @param channels <tt>int</tt>: The total number of input channels to encode.
|
||||
* This must be no more than 255.
|
||||
* @param mapping_family <tt>int</tt>: The mapping family to use for selecting
|
||||
* the appropriate projection.
|
||||
* @returns The size in bytes on success, or a negative error code
|
||||
* (see @ref opus_errorcodes) on error.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_projection_ambisonics_encoder_get_size(
|
||||
int channels,
|
||||
int mapping_family
|
||||
);
|
||||
|
||||
|
||||
/** Allocates and initializes a projection encoder state.
|
||||
* Call opus_projection_encoder_destroy() to release
|
||||
* this object when finished.
|
||||
* @param Fs <tt>opus_int32</tt>: Sampling rate of the input signal (in Hz).
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param channels <tt>int</tt>: Number of channels in the input signal.
|
||||
* This must be at most 255.
|
||||
* It may be greater than the number of
|
||||
* coded channels (<code>streams +
|
||||
* coupled_streams</code>).
|
||||
* @param mapping_family <tt>int</tt>: The mapping family to use for selecting
|
||||
* the appropriate projection.
|
||||
* @param[out] streams <tt>int *</tt>: The total number of streams that will
|
||||
* be encoded from the input.
|
||||
* @param[out] coupled_streams <tt>int *</tt>: Number of coupled (2 channel)
|
||||
* streams that will be encoded from the input.
|
||||
* @param application <tt>int</tt>: The target encoder application.
|
||||
* This must be one of the following:
|
||||
* <dl>
|
||||
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||
* <dd>Favor faithfulness to the original input.</dd>
|
||||
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||
* of operation.</dd>
|
||||
* </dl>
|
||||
* @param[out] error <tt>int *</tt>: Returns #OPUS_OK on success, or an error
|
||||
* code (see @ref opus_errorcodes) on
|
||||
* failure.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusProjectionEncoder *opus_projection_ambisonics_encoder_create(
|
||||
opus_int32 Fs,
|
||||
int channels,
|
||||
int mapping_family,
|
||||
int *streams,
|
||||
int *coupled_streams,
|
||||
int application,
|
||||
int *error
|
||||
) OPUS_ARG_NONNULL(4) OPUS_ARG_NONNULL(5);
|
||||
|
||||
|
||||
/** Initialize a previously allocated projection encoder state.
|
||||
* The memory pointed to by \a st must be at least the size returned by
|
||||
* opus_projection_ambisonics_encoder_get_size().
|
||||
* This is intended for applications which use their own allocator instead of
|
||||
* malloc.
|
||||
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||
* @see opus_projection_ambisonics_encoder_create
|
||||
* @see opus_projection_ambisonics_encoder_get_size
|
||||
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state to initialize.
|
||||
* @param Fs <tt>opus_int32</tt>: Sampling rate of the input signal (in Hz).
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param channels <tt>int</tt>: Number of channels in the input signal.
|
||||
* This must be at most 255.
|
||||
* It may be greater than the number of
|
||||
* coded channels (<code>streams +
|
||||
* coupled_streams</code>).
|
||||
* @param streams <tt>int</tt>: The total number of streams to encode from the
|
||||
* input.
|
||||
* This must be no more than the number of channels.
|
||||
* @param coupled_streams <tt>int</tt>: Number of coupled (2 channel) streams
|
||||
* to encode.
|
||||
* This must be no larger than the total
|
||||
* number of streams.
|
||||
* Additionally, The total number of
|
||||
* encoded channels (<code>streams +
|
||||
* coupled_streams</code>) must be no
|
||||
* more than the number of input channels.
|
||||
* @param application <tt>int</tt>: The target encoder application.
|
||||
* This must be one of the following:
|
||||
* <dl>
|
||||
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||
* <dd>Favor faithfulness to the original input.</dd>
|
||||
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||
* of operation.</dd>
|
||||
* </dl>
|
||||
* @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes)
|
||||
* on failure.
|
||||
*/
|
||||
OPUS_EXPORT int opus_projection_ambisonics_encoder_init(
|
||||
OpusProjectionEncoder *st,
|
||||
opus_int32 Fs,
|
||||
int channels,
|
||||
int mapping_family,
|
||||
int *streams,
|
||||
int *coupled_streams,
|
||||
int application
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(5) OPUS_ARG_NONNULL(6);
|
||||
|
||||
|
||||
/** Encodes a projection Opus frame.
|
||||
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state.
|
||||
* @param[in] pcm <tt>const opus_int16*</tt>: The input signal as interleaved
|
||||
* samples.
|
||||
* This must contain
|
||||
* <code>frame_size*channels</code>
|
||||
* samples.
|
||||
* @param frame_size <tt>int</tt>: Number of samples per channel in the input
|
||||
* signal.
|
||||
* This must be an Opus frame size for the
|
||||
* encoder's sampling rate.
|
||||
* For example, at 48 kHz the permitted values
|
||||
* are 120, 240, 480, 960, 1920, and 2880.
|
||||
* Passing in a duration of less than 10 ms
|
||||
* (480 samples at 48 kHz) will prevent the
|
||||
* encoder from using the LPC or hybrid modes.
|
||||
* @param[out] data <tt>unsigned char*</tt>: Output payload.
|
||||
* This must contain storage for at
|
||||
* least \a max_data_bytes.
|
||||
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||
* memory for the output
|
||||
* payload. This may be
|
||||
* used to impose an upper limit on
|
||||
* the instant bitrate, but should
|
||||
* not be used as the only bitrate
|
||||
* control. Use #OPUS_SET_BITRATE to
|
||||
* control the bitrate.
|
||||
* @returns The length of the encoded packet (in bytes) on success or a
|
||||
* negative error code (see @ref opus_errorcodes) on failure.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_encode(
|
||||
OpusProjectionEncoder *st,
|
||||
const opus_int16 *pcm,
|
||||
int frame_size,
|
||||
unsigned char *data,
|
||||
opus_int32 max_data_bytes
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||
|
||||
|
||||
/** Encodes a projection Opus frame from floating point input.
|
||||
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state.
|
||||
* @param[in] pcm <tt>const float*</tt>: The input signal as interleaved
|
||||
* samples with a normal range of
|
||||
* +/-1.0.
|
||||
* Samples with a range beyond +/-1.0
|
||||
* are supported but will be clipped by
|
||||
* decoders using the integer API and
|
||||
* should only be used if it is known
|
||||
* that the far end supports extended
|
||||
* dynamic range.
|
||||
* This must contain
|
||||
* <code>frame_size*channels</code>
|
||||
* samples.
|
||||
* @param frame_size <tt>int</tt>: Number of samples per channel in the input
|
||||
* signal.
|
||||
* This must be an Opus frame size for the
|
||||
* encoder's sampling rate.
|
||||
* For example, at 48 kHz the permitted values
|
||||
* are 120, 240, 480, 960, 1920, and 2880.
|
||||
* Passing in a duration of less than 10 ms
|
||||
* (480 samples at 48 kHz) will prevent the
|
||||
* encoder from using the LPC or hybrid modes.
|
||||
* @param[out] data <tt>unsigned char*</tt>: Output payload.
|
||||
* This must contain storage for at
|
||||
* least \a max_data_bytes.
|
||||
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||
* memory for the output
|
||||
* payload. This may be
|
||||
* used to impose an upper limit on
|
||||
* the instant bitrate, but should
|
||||
* not be used as the only bitrate
|
||||
* control. Use #OPUS_SET_BITRATE to
|
||||
* control the bitrate.
|
||||
* @returns The length of the encoded packet (in bytes) on success or a
|
||||
* negative error code (see @ref opus_errorcodes) on failure.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_encode_float(
|
||||
OpusProjectionEncoder *st,
|
||||
const float *pcm,
|
||||
int frame_size,
|
||||
unsigned char *data,
|
||||
opus_int32 max_data_bytes
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||
|
||||
|
||||
/** Frees an <code>OpusProjectionEncoder</code> allocated by
|
||||
* opus_projection_ambisonics_encoder_create().
|
||||
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state to be freed.
|
||||
*/
|
||||
OPUS_EXPORT void opus_projection_encoder_destroy(OpusProjectionEncoder *st);
|
||||
|
||||
|
||||
/** Perform a CTL function on a projection Opus encoder.
|
||||
*
|
||||
* Generally the request and subsequent arguments are generated by a
|
||||
* convenience macro.
|
||||
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state.
|
||||
* @param request This and all remaining parameters should be replaced by one
|
||||
* of the convenience macros in @ref opus_genericctls,
|
||||
* @ref opus_encoderctls, @ref opus_multistream_ctls, or
|
||||
* @ref opus_projection_ctls
|
||||
* @see opus_genericctls
|
||||
* @see opus_encoderctls
|
||||
* @see opus_multistream_ctls
|
||||
* @see opus_projection_ctls
|
||||
*/
|
||||
OPUS_EXPORT int opus_projection_encoder_ctl(OpusProjectionEncoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||
|
||||
|
||||
/**@}*/
|
||||
|
||||
/**\name Projection decoder functions */
|
||||
/**@{*/
|
||||
|
||||
/** Gets the size of an <code>OpusProjectionDecoder</code> structure.
|
||||
* @param channels <tt>int</tt>: The total number of output channels.
|
||||
* This must be no more than 255.
|
||||
* @param streams <tt>int</tt>: The total number of streams coded in the
|
||||
* input.
|
||||
* This must be no more than 255.
|
||||
* @param coupled_streams <tt>int</tt>: Number streams to decode as coupled
|
||||
* (2 channel) streams.
|
||||
* This must be no larger than the total
|
||||
* number of streams.
|
||||
* Additionally, The total number of
|
||||
* coded channels (<code>streams +
|
||||
* coupled_streams</code>) must be no
|
||||
* more than 255.
|
||||
* @returns The size in bytes on success, or a negative error code
|
||||
* (see @ref opus_errorcodes) on error.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_projection_decoder_get_size(
|
||||
int channels,
|
||||
int streams,
|
||||
int coupled_streams
|
||||
);
|
||||
|
||||
|
||||
/** Allocates and initializes a projection decoder state.
|
||||
* Call opus_projection_decoder_destroy() to release
|
||||
* this object when finished.
|
||||
* @param Fs <tt>opus_int32</tt>: Sampling rate to decode at (in Hz).
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param channels <tt>int</tt>: Number of channels to output.
|
||||
* This must be at most 255.
|
||||
* It may be different from the number of coded
|
||||
* channels (<code>streams +
|
||||
* coupled_streams</code>).
|
||||
* @param streams <tt>int</tt>: The total number of streams coded in the
|
||||
* input.
|
||||
* This must be no more than 255.
|
||||
* @param coupled_streams <tt>int</tt>: Number of streams to decode as coupled
|
||||
* (2 channel) streams.
|
||||
* This must be no larger than the total
|
||||
* number of streams.
|
||||
* Additionally, The total number of
|
||||
* coded channels (<code>streams +
|
||||
* coupled_streams</code>) must be no
|
||||
* more than 255.
|
||||
* @param[in] demixing_matrix <tt>const unsigned char[demixing_matrix_size]</tt>: Demixing matrix
|
||||
* that mapping from coded channels to output channels,
|
||||
* as described in @ref opus_projection and
|
||||
* @ref opus_projection_ctls.
|
||||
* @param demixing_matrix_size <tt>opus_int32</tt>: The size in bytes of the
|
||||
* demixing matrix, as
|
||||
* described in @ref
|
||||
* opus_projection_ctls.
|
||||
* @param[out] error <tt>int *</tt>: Returns #OPUS_OK on success, or an error
|
||||
* code (see @ref opus_errorcodes) on
|
||||
* failure.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusProjectionDecoder *opus_projection_decoder_create(
|
||||
opus_int32 Fs,
|
||||
int channels,
|
||||
int streams,
|
||||
int coupled_streams,
|
||||
unsigned char *demixing_matrix,
|
||||
opus_int32 demixing_matrix_size,
|
||||
int *error
|
||||
) OPUS_ARG_NONNULL(5);
|
||||
|
||||
|
||||
/** Intialize a previously allocated projection decoder state object.
|
||||
* The memory pointed to by \a st must be at least the size returned by
|
||||
* opus_projection_decoder_get_size().
|
||||
* This is intended for applications which use their own allocator instead of
|
||||
* malloc.
|
||||
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||
* @see opus_projection_decoder_create
|
||||
* @see opus_projection_deocder_get_size
|
||||
* @param st <tt>OpusProjectionDecoder*</tt>: Projection encoder state to initialize.
|
||||
* @param Fs <tt>opus_int32</tt>: Sampling rate to decode at (in Hz).
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param channels <tt>int</tt>: Number of channels to output.
|
||||
* This must be at most 255.
|
||||
* It may be different from the number of coded
|
||||
* channels (<code>streams +
|
||||
* coupled_streams</code>).
|
||||
* @param streams <tt>int</tt>: The total number of streams coded in the
|
||||
* input.
|
||||
* This must be no more than 255.
|
||||
* @param coupled_streams <tt>int</tt>: Number of streams to decode as coupled
|
||||
* (2 channel) streams.
|
||||
* This must be no larger than the total
|
||||
* number of streams.
|
||||
* Additionally, The total number of
|
||||
* coded channels (<code>streams +
|
||||
* coupled_streams</code>) must be no
|
||||
* more than 255.
|
||||
* @param[in] demixing_matrix <tt>const unsigned char[demixing_matrix_size]</tt>: Demixing matrix
|
||||
* that mapping from coded channels to output channels,
|
||||
* as described in @ref opus_projection and
|
||||
* @ref opus_projection_ctls.
|
||||
* @param demixing_matrix_size <tt>opus_int32</tt>: The size in bytes of the
|
||||
* demixing matrix, as
|
||||
* described in @ref
|
||||
* opus_projection_ctls.
|
||||
* @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes)
|
||||
* on failure.
|
||||
*/
|
||||
OPUS_EXPORT int opus_projection_decoder_init(
|
||||
OpusProjectionDecoder *st,
|
||||
opus_int32 Fs,
|
||||
int channels,
|
||||
int streams,
|
||||
int coupled_streams,
|
||||
unsigned char *demixing_matrix,
|
||||
opus_int32 demixing_matrix_size
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6);
|
||||
|
||||
|
||||
/** Decode a projection Opus packet.
|
||||
* @param st <tt>OpusProjectionDecoder*</tt>: Projection decoder state.
|
||||
* @param[in] data <tt>const unsigned char*</tt>: Input payload.
|
||||
* Use a <code>NULL</code>
|
||||
* pointer to indicate packet
|
||||
* loss.
|
||||
* @param len <tt>opus_int32</tt>: Number of bytes in payload.
|
||||
* @param[out] pcm <tt>opus_int16*</tt>: Output signal, with interleaved
|
||||
* samples.
|
||||
* This must contain room for
|
||||
* <code>frame_size*channels</code>
|
||||
* samples.
|
||||
* @param frame_size <tt>int</tt>: The number of samples per channel of
|
||||
* available space in \a pcm.
|
||||
* If this is less than the maximum packet duration
|
||||
* (120 ms; 5760 for 48kHz), this function will not be capable
|
||||
* of decoding some packets. In the case of PLC (data==NULL)
|
||||
* or FEC (decode_fec=1), then frame_size needs to be exactly
|
||||
* the duration of audio that is missing, otherwise the
|
||||
* decoder will not be in the optimal state to decode the
|
||||
* next incoming packet. For the PLC and FEC cases, frame_size
|
||||
* <b>must</b> be a multiple of 2.5 ms.
|
||||
* @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
|
||||
* forward error correction data be decoded.
|
||||
* If no such data is available, the frame is
|
||||
* decoded as if it were lost.
|
||||
* @returns Number of samples decoded on success or a negative error code
|
||||
* (see @ref opus_errorcodes) on failure.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_decode(
|
||||
OpusProjectionDecoder *st,
|
||||
const unsigned char *data,
|
||||
opus_int32 len,
|
||||
opus_int16 *pcm,
|
||||
int frame_size,
|
||||
int decode_fec
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||
|
||||
|
||||
/** Decode a projection Opus packet with floating point output.
|
||||
* @param st <tt>OpusProjectionDecoder*</tt>: Projection decoder state.
|
||||
* @param[in] data <tt>const unsigned char*</tt>: Input payload.
|
||||
* Use a <code>NULL</code>
|
||||
* pointer to indicate packet
|
||||
* loss.
|
||||
* @param len <tt>opus_int32</tt>: Number of bytes in payload.
|
||||
* @param[out] pcm <tt>opus_int16*</tt>: Output signal, with interleaved
|
||||
* samples.
|
||||
* This must contain room for
|
||||
* <code>frame_size*channels</code>
|
||||
* samples.
|
||||
* @param frame_size <tt>int</tt>: The number of samples per channel of
|
||||
* available space in \a pcm.
|
||||
* If this is less than the maximum packet duration
|
||||
* (120 ms; 5760 for 48kHz), this function will not be capable
|
||||
* of decoding some packets. In the case of PLC (data==NULL)
|
||||
* or FEC (decode_fec=1), then frame_size needs to be exactly
|
||||
* the duration of audio that is missing, otherwise the
|
||||
* decoder will not be in the optimal state to decode the
|
||||
* next incoming packet. For the PLC and FEC cases, frame_size
|
||||
* <b>must</b> be a multiple of 2.5 ms.
|
||||
* @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
|
||||
* forward error correction data be decoded.
|
||||
* If no such data is available, the frame is
|
||||
* decoded as if it were lost.
|
||||
* @returns Number of samples decoded on success or a negative error code
|
||||
* (see @ref opus_errorcodes) on failure.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_decode_float(
|
||||
OpusProjectionDecoder *st,
|
||||
const unsigned char *data,
|
||||
opus_int32 len,
|
||||
float *pcm,
|
||||
int frame_size,
|
||||
int decode_fec
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||
|
||||
|
||||
/** Perform a CTL function on a projection Opus decoder.
|
||||
*
|
||||
* Generally the request and subsequent arguments are generated by a
|
||||
* convenience macro.
|
||||
* @param st <tt>OpusProjectionDecoder*</tt>: Projection decoder state.
|
||||
* @param request This and all remaining parameters should be replaced by one
|
||||
* of the convenience macros in @ref opus_genericctls,
|
||||
* @ref opus_decoderctls, @ref opus_multistream_ctls, or
|
||||
* @ref opus_projection_ctls.
|
||||
* @see opus_genericctls
|
||||
* @see opus_decoderctls
|
||||
* @see opus_multistream_ctls
|
||||
* @see opus_projection_ctls
|
||||
*/
|
||||
OPUS_EXPORT int opus_projection_decoder_ctl(OpusProjectionDecoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||
|
||||
|
||||
/** Frees an <code>OpusProjectionDecoder</code> allocated by
|
||||
* opus_projection_decoder_create().
|
||||
* @param st <tt>OpusProjectionDecoder</tt>: Projection decoder state to be freed.
|
||||
*/
|
||||
OPUS_EXPORT void opus_projection_decoder_destroy(OpusProjectionDecoder *st);
|
||||
|
||||
|
||||
/**@}*/
|
||||
|
||||
/**@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPUS_PROJECTION_H */
|
|
@ -4,7 +4,10 @@ src/opus_encoder.c \
|
|||
src/opus_multistream.c \
|
||||
src/opus_multistream_encoder.c \
|
||||
src/opus_multistream_decoder.c \
|
||||
src/repacketizer.c
|
||||
src/repacketizer.c \
|
||||
src/opus_projection_encoder.c \
|
||||
src/opus_projection_decoder.c \
|
||||
src/mapping_matrix.c
|
||||
|
||||
OPUS_SOURCES_FLOAT = \
|
||||
src/analysis.c \
|
||||
|
|
287
src/mapping_matrix.c
Normal file
287
src/mapping_matrix.c
Normal file
|
@ -0,0 +1,287 @@
|
|||
/* 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 "arch.h"
|
||||
#include "float_cast.h"
|
||||
#include "opus_private.h"
|
||||
#include "opus_defines.h"
|
||||
#include "mapping_matrix.h"
|
||||
|
||||
#ifdef ENABLE_EXPERIMENTAL_AMBISONICS
|
||||
|
||||
#define MATRIX_INDEX(nb_rows, row, col) (nb_rows * col + row)
|
||||
|
||||
int mapping_matrix_get_size(int rows, int cols)
|
||||
{
|
||||
return align(sizeof(MappingMatrix)) + rows * cols * sizeof(opus_int16);
|
||||
}
|
||||
|
||||
opus_int16 *mapping_matrix_get_data(const MappingMatrix *matrix)
|
||||
{
|
||||
return (opus_int16*)(void *)(matrix + align(sizeof(MappingMatrix)));
|
||||
}
|
||||
|
||||
void mapping_matrix_init(MappingMatrix * const matrix,
|
||||
int rows, int cols, int gain, const opus_int16 *data, opus_int32 data_size)
|
||||
{
|
||||
int i;
|
||||
opus_int16 *ptr;
|
||||
|
||||
#if !defined(ENABLE_ASSERTIONS)
|
||||
(void)data_size;
|
||||
#endif
|
||||
celt_assert((opus_uint32)data_size == rows * cols * sizeof(opus_int16));
|
||||
|
||||
matrix->rows = rows;
|
||||
matrix->cols = cols;
|
||||
matrix->gain = gain;
|
||||
ptr = mapping_matrix_get_data(matrix);
|
||||
for (i = 0; i < rows * cols; i++)
|
||||
{
|
||||
ptr[i] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_FLOAT_API
|
||||
void mapping_matrix_multiply_float(const MappingMatrix *matrix,
|
||||
const float *input, int input_rows,
|
||||
float *output, int output_rows,
|
||||
int frame_size)
|
||||
{
|
||||
/* Matrix data is ordered col-wise.
|
||||
* Input (x) is [n x k], output (y) is [m x k], matrix (M) is [m x n]:
|
||||
* y = M x
|
||||
*/
|
||||
opus_int16* matrix_data;
|
||||
int i, row, col;
|
||||
float matrix_cell, input_sample;
|
||||
|
||||
celt_assert(input_rows <= matrix->cols && output_rows <= matrix->rows);
|
||||
|
||||
matrix_data = mapping_matrix_get_data(matrix);
|
||||
|
||||
for (i = 0; i < frame_size; i++)
|
||||
{
|
||||
for (row = 0; row < output_rows; row++)
|
||||
{
|
||||
output[MATRIX_INDEX(output_rows, row, i)] = 0;
|
||||
for (col = 0; col < input_rows; col++)
|
||||
{
|
||||
matrix_cell = (0.000030518f)*(float)matrix_data[MATRIX_INDEX(matrix->rows, row, col)];
|
||||
input_sample = input[MATRIX_INDEX(input_rows, col, i)];
|
||||
output[MATRIX_INDEX(output_rows, row, i)] += matrix_cell * input_sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* DISABLE_FLOAT_API */
|
||||
|
||||
void mapping_matrix_multiply_short(const MappingMatrix *matrix,
|
||||
const opus_int16 *input, int input_rows,
|
||||
opus_int16 *output, int output_rows,
|
||||
int frame_size)
|
||||
{
|
||||
/* Matrix data is ordered col-wise.
|
||||
* Input (x) is [n x k], output (y) is [m x k], matrix (M) is [m x n]:
|
||||
* y = M x
|
||||
*/
|
||||
opus_int16* matrix_data;
|
||||
int i, row, col;
|
||||
|
||||
celt_assert(input_rows <= matrix->cols && output_rows <= matrix->rows);
|
||||
|
||||
matrix_data = mapping_matrix_get_data(matrix);
|
||||
|
||||
for (i = 0; i < frame_size; i++)
|
||||
{
|
||||
for (row = 0; row < output_rows; row++)
|
||||
{
|
||||
opus_int32 tmp = 0;
|
||||
for (col = 0; col < input_rows; col++)
|
||||
{
|
||||
tmp +=
|
||||
(matrix_data[MATRIX_INDEX(matrix->rows, row, col)] *
|
||||
input[MATRIX_INDEX(input_rows, col, i)]) >> 8;
|
||||
}
|
||||
output[MATRIX_INDEX(output_rows, row, i)] = (tmp + 64)>>7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MappingMatrix mapping_matrix_foa_mixing = { 6, 6, 0 };
|
||||
const opus_int16 mapping_matrix_foa_mixing_data[36] = {
|
||||
16384, 0, -16384, 23170, 0, 0, 16384, 23170,
|
||||
16384, 0, 0, 0, 16384, 0, -16384, -23170,
|
||||
0, 0, 16384, -23170, 16384, 0, 0, 0,
|
||||
0, 0, 0, 0, 32767, 0, 0, 0,
|
||||
0, 0, 0, 32767
|
||||
};
|
||||
|
||||
const MappingMatrix mapping_matrix_soa_mixing = { 11, 11, 0 };
|
||||
const opus_int16 mapping_matrix_soa_mixing_data[121] = {
|
||||
10923, 7723, 13377, -13377, 11585, 9459, 7723, -16384,
|
||||
-6689, 0, 0, 10923, 7723, 13377, 13377, -11585,
|
||||
9459, 7723, 16384, -6689, 0, 0, 10923, -15447,
|
||||
13377, 0, 0, -18919, 7723, 0, 13377, 0,
|
||||
0, 10923, 7723, -13377, -13377, 11585, -9459, 7723,
|
||||
16384, -6689, 0, 0, 10923, -7723, 0, 13377,
|
||||
-16384, 0, -15447, 0, 9459, 0, 0, 10923,
|
||||
-7723, 0, -13377, 16384, 0, -15447, 0, 9459,
|
||||
0, 0, 10923, 15447, 0, 0, 0, 0,
|
||||
-15447, 0, -18919, 0, 0, 10923, 7723, -13377,
|
||||
13377, -11585, -9459, 7723, -16384, -6689, 0, 0,
|
||||
10923, -15447, -13377, 0, 0, 18919, 7723, 0,
|
||||
13377, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 32767, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32767
|
||||
};
|
||||
|
||||
const MappingMatrix mapping_matrix_toa_mixing = { 18, 18, 0 };
|
||||
const opus_int16 mapping_matrix_toa_mixing_data[324] = {
|
||||
8208, 0, -881, 14369, 0, 0, -8192, -4163,
|
||||
13218, 0, 0, 0, 11095, -8836, -6218, 14833,
|
||||
0, 0, 8208, -10161, 881, 10161, -13218, -2944,
|
||||
-8192, 2944, 0, -10488, -6218, 6248, -11095, -6248,
|
||||
0, -10488, 0, 0, 8208, 10161, 881, -10161,
|
||||
-13218, 2944, -8192, -2944, 0, 10488, -6218, -6248,
|
||||
-11095, 6248, 0, 10488, 0, 0, 8176, 5566,
|
||||
-11552, 5566, 9681, -11205, 8192, -11205, 0, 4920,
|
||||
-15158, 9756, -3334, 9756, 0, -4920, 0, 0,
|
||||
8176, 7871, 11552, 0, 0, 15846, 8192, 0,
|
||||
-9681, -6958, 0, 13797, 3334, 0, -15158, 0,
|
||||
0, 0, 8176, 0, 11552, 7871, 0, 0,
|
||||
8192, 15846, 9681, 0, 0, 0, 3334, 13797,
|
||||
15158, 6958, 0, 0, 8176, 5566, -11552, -5566,
|
||||
-9681, -11205, 8192, 11205, 0, 4920, 15158, 9756,
|
||||
-3334, -9756, 0, 4920, 0, 0, 8208, 14369,
|
||||
-881, 0, 0, -4163, -8192, 0, -13218, -14833,
|
||||
0, -8836, 11095, 0, 6218, 0, 0, 0,
|
||||
8208, 10161, 881, 10161, 13218, 2944, -8192, 2944,
|
||||
0, 10488, 6218, -6248, -11095, -6248, 0, -10488,
|
||||
0, 0, 8208, -14369, -881, 0, 0, 4163,
|
||||
-8192, 0, -13218, 14833, 0, 8836, 11095, 0,
|
||||
6218, 0, 0, 0, 8208, 0, -881, -14369,
|
||||
0, 0, -8192, 4163, 13218, 0, 0, 0,
|
||||
11095, 8836, -6218, -14833, 0, 0, 8176, -5566,
|
||||
-11552, 5566, -9681, 11205, 8192, -11205, 0, -4920,
|
||||
15158, -9756, -3334, 9756, 0, -4920, 0, 0,
|
||||
8176, 0, 11552, -7871, 0, 0, 8192, -15846,
|
||||
9681, 0, 0, 0, 3334, -13797, 15158, -6958,
|
||||
0, 0, 8176, -7871, 11552, 0, 0, -15846,
|
||||
8192, 0, -9681, 6958, 0, -13797, 3334, 0,
|
||||
-15158, 0, 0, 0, 8176, -5566, -11552, -5566,
|
||||
9681, 11205, 8192, 11205, 0, -4920, -15158, -9756,
|
||||
-3334, -9756, 0, 4920, 0, 0, 8208, -10161,
|
||||
881, -10161, 13218, -2944, -8192, -2944, 0, -10488,
|
||||
6218, 6248, -11095, 6248, 0, 10488, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32767, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 32767
|
||||
};
|
||||
|
||||
const MappingMatrix mapping_matrix_foa_demixing = { 6, 6, 0 };
|
||||
const opus_int16 mapping_matrix_foa_demixing_data[36] = {
|
||||
16384, 16384, 16384, 16384, 0, 0, 0, 23170,
|
||||
0, -23170, 0, 0, -16384, 16384, -16384, 16384,
|
||||
0, 0, 23170, 0, -23170, 0, 0, 0,
|
||||
0, 0, 0, 0, 32767, 0, 0, 0,
|
||||
0, 0, 0, 32767
|
||||
};
|
||||
|
||||
const MappingMatrix mapping_matrix_soa_demixing = { 11, 11, 3050 };
|
||||
const opus_int16 mapping_matrix_soa_demixing_data[121] = {
|
||||
2771, 2771, 2771, 2771, 2771, 2771, 2771, 2771,
|
||||
2771, 0, 0, 10033, 10033, -20066, 10033, 14189,
|
||||
14189, -28378, 10033, -20066, 0, 0, 3393, 3393,
|
||||
3393, -3393, 0, 0, 0, -3393, -3393, 0,
|
||||
0, -17378, 17378, 0, -17378, -24576, 24576, 0,
|
||||
17378, 0, 0, 0, -14189, 14189, 0, -14189,
|
||||
-28378, 28378, 0, 14189, 0, 0, 0, 2399,
|
||||
2399, -4799, -2399, 0, 0, 0, -2399, 4799,
|
||||
0, 0, 1959, 1959, 1959, 1959, -3918, -3918,
|
||||
-3918, 1959, 1959, 0, 0, -4156, 4156, 0,
|
||||
4156, 0, 0, 0, -4156, 0, 0, 0,
|
||||
8192, 8192, -16384, 8192, 16384, 16384, -32768, 8192,
|
||||
-16384, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 8312, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
8312
|
||||
};
|
||||
|
||||
const MappingMatrix mapping_matrix_toa_demixing = { 18, 18, 0 };
|
||||
const opus_int16 mapping_matrix_toa_demixing_data[324] = {
|
||||
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
|
||||
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
|
||||
0, 0, 0, -9779, 9779, 6263, 8857, 0,
|
||||
6263, 13829, 9779, -13829, 0, -6263, 0, -8857,
|
||||
-6263, -9779, 0, 0, -3413, 3413, 3413, -11359,
|
||||
11359, 11359, -11359, -3413, 3413, -3413, -3413, -11359,
|
||||
11359, 11359, -11359, 3413, 0, 0, 13829, 9779,
|
||||
-9779, 6263, 0, 8857, -6263, 0, 9779, 0,
|
||||
-13829, 6263, -8857, 0, -6263, -9779, 0, 0,
|
||||
0, -15617, -15617, 6406, 0, 0, -6406, 0,
|
||||
15617, 0, 0, -6406, 0, 0, 6406, 15617,
|
||||
0, 0, 0, -5003, 5003, -10664, 15081, 0,
|
||||
-10664, -7075, 5003, 7075, 0, 10664, 0, -15081,
|
||||
10664, -5003, 0, 0, -8176, -8176, -8176, 8208,
|
||||
8208, 8208, 8208, -8176, -8176, -8176, -8176, 8208,
|
||||
8208, 8208, 8208, -8176, 0, 0, -7075, 5003,
|
||||
-5003, -10664, 0, 15081, 10664, 0, 5003, 0,
|
||||
7075, -10664, -15081, 0, 10664, -5003, 0, 0,
|
||||
15617, 0, 0, 0, -6406, 6406, 0, -15617,
|
||||
0, -15617, 15617, 0, 6406, -6406, 0, 0,
|
||||
0, 0, 0, -11393, 11393, 2993, -4233, 0,
|
||||
2993, -16112, 11393, 16112, 0, -2993, 0, 4233,
|
||||
-2993, -11393, 0, 0, 0, -9974, -9974, -13617,
|
||||
0, 0, 13617, 0, 9974, 0, 0, 13617,
|
||||
0, 0, -13617, 9974, 0, 0, 0, 5579,
|
||||
-5579, 10185, 14403, 0, 10185, -7890, -5579, 7890,
|
||||
0, -10185, 0, -14403, -10185, 5579, 0, 0,
|
||||
11826, -11826, -11826, -901, 901, 901, -901, 11826,
|
||||
-11826, 11826, 11826, -901, 901, 901, -901, -11826,
|
||||
0, 0, -7890, -5579, 5579, 10185, 0, 14403,
|
||||
-10185, 0, -5579, 0, 7890, 10185, -14403, 0,
|
||||
-10185, 5579, 0, 0, -9974, 0, 0, 0,
|
||||
-13617, 13617, 0, 9974, 0, 9974, -9974, 0,
|
||||
13617, -13617, 0, 0, 0, 0, 16112, -11393,
|
||||
11393, -2993, 0, 4233, 2993, 0, -11393, 0,
|
||||
-16112, -2993, -4233, 0, 2993, 11393, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32767, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 32767
|
||||
};
|
||||
|
||||
#endif /* ENABLE_EXPERIMENTAL_AMBISONICS */
|
115
src/mapping_matrix.h
Normal file
115
src/mapping_matrix.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mapping_matrix.h
|
||||
* @brief Opus reference implementation mapping matrix API
|
||||
*/
|
||||
|
||||
#ifndef MAPPING_MATRIX_H
|
||||
#define MAPPING_MATRIX_H
|
||||
|
||||
#ifdef ENABLE_EXPERIMENTAL_AMBISONICS
|
||||
|
||||
#include "opus_types.h"
|
||||
#include "opus_projection.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct MappingMatrix
|
||||
{
|
||||
int rows;
|
||||
int cols;
|
||||
int gain; /* in dB. S7.8-format. */
|
||||
/* Matrix cell data goes here using col-wise ordering. */
|
||||
} MappingMatrix;
|
||||
|
||||
int mapping_matrix_get_size(int rows, int cols);
|
||||
|
||||
opus_int16 *mapping_matrix_get_data(const MappingMatrix *matrix);
|
||||
|
||||
void mapping_matrix_init(
|
||||
MappingMatrix * const st,
|
||||
int rows,
|
||||
int cols,
|
||||
int gain,
|
||||
const opus_int16 *data,
|
||||
opus_int32 data_size
|
||||
);
|
||||
|
||||
#ifndef DISABLE_FLOAT_API
|
||||
void mapping_matrix_multiply_float(
|
||||
const MappingMatrix *matrix,
|
||||
const float *input,
|
||||
int input_rows,
|
||||
float *output,
|
||||
int output_rows,
|
||||
int frame_size
|
||||
);
|
||||
#endif /* DISABLE_FLOAT_API */
|
||||
|
||||
void mapping_matrix_multiply_short(
|
||||
const MappingMatrix *matrix,
|
||||
const opus_int16 *input,
|
||||
int input_rows,
|
||||
opus_int16 *output,
|
||||
int output_rows,
|
||||
int frame_size
|
||||
);
|
||||
|
||||
/* Pre-computed mixing and demixing matrices for 1st to 3rd-order ambisonics.
|
||||
* foa: first-order ambisonics
|
||||
* soa: second-order ambisonics
|
||||
* toa: third-order ambisonics
|
||||
*/
|
||||
extern const MappingMatrix mapping_matrix_foa_mixing;
|
||||
extern const opus_int16 mapping_matrix_foa_mixing_data[36];
|
||||
|
||||
extern const MappingMatrix mapping_matrix_soa_mixing;
|
||||
extern const opus_int16 mapping_matrix_soa_mixing_data[121];
|
||||
|
||||
extern const MappingMatrix mapping_matrix_toa_mixing;
|
||||
extern const opus_int16 mapping_matrix_toa_mixing_data[324];
|
||||
|
||||
extern const MappingMatrix mapping_matrix_foa_demixing;
|
||||
extern const opus_int16 mapping_matrix_foa_demixing_data[36];
|
||||
|
||||
extern const MappingMatrix mapping_matrix_soa_demixing;
|
||||
extern const opus_int16 mapping_matrix_soa_demixing_data[121];
|
||||
|
||||
extern const MappingMatrix mapping_matrix_toa_demixing;
|
||||
extern const opus_int16 mapping_matrix_toa_demixing_data[324];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ENABLE_EXPERIMENTAL_AMBISONICS */
|
||||
|
||||
#endif /* MAPPING_MATRIX_H */
|
|
@ -37,14 +37,6 @@
|
|||
#include "float_cast.h"
|
||||
#include "os_support.h"
|
||||
|
||||
struct OpusMSDecoder {
|
||||
ChannelLayout layout;
|
||||
/* Decoder states go here */
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* DECODER */
|
||||
|
||||
opus_int32 opus_multistream_decoder_get_size(int nb_streams, int nb_coupled_streams)
|
||||
|
@ -408,15 +400,13 @@ int opus_multistream_decode_float(
|
|||
}
|
||||
#endif
|
||||
|
||||
int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...)
|
||||
int opus_multistream_decoder_ctl_va_list(OpusMSDecoder *st, int request,
|
||||
va_list ap)
|
||||
{
|
||||
va_list ap;
|
||||
int coupled_size, mono_size;
|
||||
char *ptr;
|
||||
int ret = OPUS_OK;
|
||||
|
||||
va_start(ap, request);
|
||||
|
||||
coupled_size = opus_decoder_get_size(2);
|
||||
mono_size = opus_decoder_get_size(1);
|
||||
ptr = (char*)st + align(sizeof(OpusMSDecoder));
|
||||
|
@ -525,14 +515,20 @@ int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...)
|
|||
ret = OPUS_UNIMPLEMENTED;
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
return ret;
|
||||
bad_arg:
|
||||
va_end(ap);
|
||||
return OPUS_BAD_ARG;
|
||||
}
|
||||
|
||||
int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list ap;
|
||||
va_start(ap, request);
|
||||
ret = opus_multistream_decoder_ctl_va_list(st, request, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void opus_multistream_decoder_destroy(OpusMSDecoder *st)
|
||||
{
|
||||
|
|
|
@ -70,28 +70,6 @@ typedef void (*opus_copy_channel_in_func)(
|
|||
int frame_size
|
||||
);
|
||||
|
||||
typedef enum {
|
||||
MAPPING_TYPE_NONE,
|
||||
MAPPING_TYPE_SURROUND
|
||||
#ifdef ENABLE_EXPERIMENTAL_AMBISONICS
|
||||
, /* Do not include comma at end of enumerator list */
|
||||
MAPPING_TYPE_AMBISONICS
|
||||
#endif
|
||||
} MappingType;
|
||||
|
||||
struct OpusMSEncoder {
|
||||
ChannelLayout layout;
|
||||
int arch;
|
||||
int lfe_stream;
|
||||
int application;
|
||||
int variable_duration;
|
||||
MappingType mapping_type;
|
||||
opus_int32 bitrate_bps;
|
||||
/* Encoder states go here */
|
||||
/* then opus_val32 window_mem[channels*120]; */
|
||||
/* then opus_val32 preemph_mem[channels]; */
|
||||
};
|
||||
|
||||
static opus_val32 *ms_get_preemph_mem(OpusMSEncoder *st)
|
||||
{
|
||||
int s;
|
||||
|
@ -1196,15 +1174,13 @@ int opus_multistream_encode(
|
|||
}
|
||||
#endif
|
||||
|
||||
int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
|
||||
int opus_multistream_encoder_ctl_va_list(OpusMSEncoder *st, int request,
|
||||
va_list ap)
|
||||
{
|
||||
va_list ap;
|
||||
int coupled_size, mono_size;
|
||||
char *ptr;
|
||||
int ret = OPUS_OK;
|
||||
|
||||
va_start(ap, request);
|
||||
|
||||
coupled_size = opus_encoder_get_size(2);
|
||||
mono_size = opus_encoder_get_size(1);
|
||||
ptr = (char*)st + align(sizeof(OpusMSEncoder));
|
||||
|
@ -1392,14 +1368,21 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
|
|||
ret = OPUS_UNIMPLEMENTED;
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
return ret;
|
||||
bad_arg:
|
||||
va_end(ap);
|
||||
return OPUS_BAD_ARG;
|
||||
}
|
||||
|
||||
int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list ap;
|
||||
va_start(ap, request);
|
||||
ret = opus_multistream_encoder_ctl_va_list(st, request, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void opus_multistream_encoder_destroy(OpusMSEncoder *st)
|
||||
{
|
||||
opus_free(st);
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "opus.h"
|
||||
#include "celt.h"
|
||||
|
||||
#include <stdarg.h> /* va_list */
|
||||
#include <stddef.h> /* offsetof */
|
||||
|
||||
struct OpusRepacketizer {
|
||||
|
@ -50,6 +51,38 @@ typedef struct ChannelLayout {
|
|||
unsigned char mapping[256];
|
||||
} ChannelLayout;
|
||||
|
||||
typedef enum {
|
||||
MAPPING_TYPE_NONE,
|
||||
MAPPING_TYPE_SURROUND
|
||||
#ifdef ENABLE_EXPERIMENTAL_AMBISONICS
|
||||
, /* Do not include comma at end of enumerator list */
|
||||
MAPPING_TYPE_AMBISONICS
|
||||
#endif /* ENABLE_EXPERIMENTAL_AMBISONICS */
|
||||
} MappingType;
|
||||
|
||||
struct OpusMSEncoder {
|
||||
ChannelLayout layout;
|
||||
int arch;
|
||||
int lfe_stream;
|
||||
int application;
|
||||
int variable_duration;
|
||||
MappingType mapping_type;
|
||||
opus_int32 bitrate_bps;
|
||||
/* Encoder states go here */
|
||||
/* then opus_val32 window_mem[channels*120]; */
|
||||
/* then opus_val32 preemph_mem[channels]; */
|
||||
};
|
||||
|
||||
struct OpusMSDecoder {
|
||||
ChannelLayout layout;
|
||||
/* Decoder states go here */
|
||||
};
|
||||
|
||||
int opus_multistream_encoder_ctl_va_list(struct OpusMSEncoder *st, int request,
|
||||
va_list ap);
|
||||
int opus_multistream_decoder_ctl_va_list(struct OpusMSDecoder *st, int request,
|
||||
va_list ap);
|
||||
|
||||
int validate_layout(const ChannelLayout *layout);
|
||||
int get_left_channel(const ChannelLayout *layout, int stream_id, int prev);
|
||||
int get_right_channel(const ChannelLayout *layout, int stream_id, int prev);
|
||||
|
|
234
src/opus_projection_decoder.c
Normal file
234
src/opus_projection_decoder.c
Normal file
|
@ -0,0 +1,234 @@
|
|||
/* 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 "mathops.h"
|
||||
#include "os_support.h"
|
||||
#include "opus_private.h"
|
||||
#include "opus_defines.h"
|
||||
#include "opus_projection.h"
|
||||
#include "opus_multistream.h"
|
||||
#include "mapping_matrix.h"
|
||||
#include "stack_alloc.h"
|
||||
|
||||
#ifdef ENABLE_EXPERIMENTAL_AMBISONICS
|
||||
|
||||
struct OpusProjectionDecoder
|
||||
{
|
||||
int demixing_matrix_size_in_bytes;
|
||||
/* Encoder states go here */
|
||||
};
|
||||
|
||||
static MappingMatrix *get_demixing_matrix(OpusProjectionDecoder *st)
|
||||
{
|
||||
return (MappingMatrix *)((char*)st + align(sizeof(OpusProjectionDecoder)));
|
||||
}
|
||||
|
||||
static OpusMSDecoder *get_multistream_decoder(OpusProjectionDecoder *st)
|
||||
{
|
||||
return (OpusMSDecoder *)((char*)st + align(sizeof(OpusProjectionDecoder) +
|
||||
st->demixing_matrix_size_in_bytes));
|
||||
}
|
||||
|
||||
opus_int32 opus_projection_decoder_get_size(int channels, int streams,
|
||||
int coupled_streams)
|
||||
{
|
||||
opus_int32 matrix_size;
|
||||
opus_int32 decoder_size;
|
||||
|
||||
matrix_size =
|
||||
mapping_matrix_get_size(streams + coupled_streams, channels);
|
||||
decoder_size = opus_multistream_decoder_get_size(streams, coupled_streams);
|
||||
if (!decoder_size)
|
||||
return 0;
|
||||
|
||||
return align(sizeof(OpusProjectionDecoder) + matrix_size + decoder_size);
|
||||
}
|
||||
|
||||
int opus_projection_decoder_init(OpusProjectionDecoder *st, opus_int32 Fs,
|
||||
int channels, int streams, int coupled_streams,
|
||||
unsigned char *demixing_matrix, opus_int32 demixing_matrix_size)
|
||||
{
|
||||
int nb_input_streams;
|
||||
opus_int32 expected_matrix_size;
|
||||
int i, ret;
|
||||
unsigned char mapping[255];
|
||||
VARDECL(opus_int16, buf);
|
||||
ALLOC_STACK;
|
||||
|
||||
/* Verify supplied matrix size. */
|
||||
nb_input_streams = streams + coupled_streams;
|
||||
expected_matrix_size = nb_input_streams * channels * sizeof(opus_int16);
|
||||
if (expected_matrix_size != demixing_matrix_size)
|
||||
{
|
||||
RESTORE_STACK;
|
||||
return OPUS_BAD_ARG;
|
||||
}
|
||||
|
||||
/* Convert demixing matrix input into internal format. */
|
||||
ALLOC(buf, demixing_matrix_size, opus_int16);
|
||||
for (i = 0; i < nb_input_streams * channels; i++)
|
||||
{
|
||||
int s = demixing_matrix[2*i + 1] << 8 | demixing_matrix[2*i];
|
||||
s = ((s & 0xFFFF) ^ 0x8000) - 0x8000;
|
||||
buf[i] = (opus_int16)s;
|
||||
}
|
||||
|
||||
/* Assign demixing matrix. */
|
||||
st->demixing_matrix_size_in_bytes = expected_matrix_size;
|
||||
mapping_matrix_init(get_demixing_matrix(st), nb_input_streams, channels, 0,
|
||||
buf, demixing_matrix_size);
|
||||
|
||||
/* Set trivial mapping so each input channel pairs with a matrix column. */
|
||||
for (i = 0; i < channels; i++)
|
||||
{
|
||||
mapping[i] = i;
|
||||
}
|
||||
|
||||
ret = opus_multistream_decoder_init(
|
||||
get_multistream_decoder(st), Fs, channels, streams, coupled_streams, mapping);
|
||||
RESTORE_STACK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
OpusProjectionDecoder *opus_projection_decoder_create(
|
||||
opus_int32 Fs, int channels, int streams, int coupled_streams,
|
||||
unsigned char *demixing_matrix, opus_int32 demixing_matrix_size, int *error)
|
||||
{
|
||||
int size;
|
||||
int ret;
|
||||
OpusProjectionDecoder *st;
|
||||
|
||||
/* Allocate space for the projection decoder. */
|
||||
size = opus_projection_decoder_get_size(channels, streams, coupled_streams);
|
||||
if (!size) {
|
||||
if (error)
|
||||
*error = OPUS_ALLOC_FAIL;
|
||||
return NULL;
|
||||
}
|
||||
st = (OpusProjectionDecoder *)opus_alloc(size);
|
||||
if (!st)
|
||||
{
|
||||
if (error)
|
||||
*error = OPUS_ALLOC_FAIL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize projection decoder with provided settings. */
|
||||
ret = opus_projection_decoder_init(st, Fs, channels, streams, coupled_streams,
|
||||
demixing_matrix, demixing_matrix_size);
|
||||
if (ret != OPUS_OK)
|
||||
{
|
||||
opus_free(st);
|
||||
st = NULL;
|
||||
}
|
||||
if (error)
|
||||
*error = ret;
|
||||
return st;
|
||||
}
|
||||
|
||||
int opus_projection_decode(OpusProjectionDecoder *st, const unsigned char *data,
|
||||
opus_int32 len, opus_int16 *pcm, int frame_size,
|
||||
int decode_fec)
|
||||
{
|
||||
#ifdef NONTHREADSAFE_PSEUDOSTACK
|
||||
celt_fatal("Unable to use opus_projection_decode() when NONTHREADSAFE_PSEUDOSTACK is defined.");
|
||||
#endif
|
||||
MappingMatrix *matrix;
|
||||
OpusMSDecoder *ms_decoder;
|
||||
int ret;
|
||||
VARDECL(opus_int16, buf);
|
||||
ALLOC_STACK;
|
||||
|
||||
ms_decoder = get_multistream_decoder(st);
|
||||
ALLOC(buf, (ms_decoder->layout.nb_streams + ms_decoder->layout.nb_coupled_streams) *
|
||||
frame_size, opus_int16);
|
||||
ret = opus_multistream_decode(ms_decoder, data, len, buf, frame_size,
|
||||
decode_fec);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
frame_size = ret;
|
||||
matrix = get_demixing_matrix(st);
|
||||
mapping_matrix_multiply_short(matrix, buf,
|
||||
ms_decoder->layout.nb_streams + ms_decoder->layout.nb_coupled_streams,
|
||||
pcm, ms_decoder->layout.nb_channels, frame_size);
|
||||
RESTORE_STACK;
|
||||
return frame_size;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_FLOAT_API
|
||||
int opus_projection_decode_float(OpusProjectionDecoder *st, const unsigned char *data,
|
||||
opus_int32 len, float *pcm,
|
||||
int frame_size, int decode_fec)
|
||||
{
|
||||
#ifdef NONTHREADSAFE_PSEUDOSTACK
|
||||
celt_fatal("Unable to use opus_projection_decode_float() when NONTHREADSAFE_PSEUDOSTACK is defined.");
|
||||
#endif
|
||||
MappingMatrix *matrix;
|
||||
OpusMSDecoder *ms_decoder;
|
||||
int ret;
|
||||
VARDECL(float, buf);
|
||||
ALLOC_STACK;
|
||||
|
||||
ms_decoder = get_multistream_decoder(st);
|
||||
ALLOC(buf, (ms_decoder->layout.nb_streams + ms_decoder->layout.nb_coupled_streams) *
|
||||
frame_size, float);
|
||||
ret = opus_multistream_decode_float(ms_decoder, data, len, buf,
|
||||
frame_size, decode_fec);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
frame_size = ret;
|
||||
matrix = get_demixing_matrix(st);
|
||||
mapping_matrix_multiply_float(matrix, buf,
|
||||
ms_decoder->layout.nb_streams + ms_decoder->layout.nb_coupled_streams,
|
||||
pcm, ms_decoder->layout.nb_channels, frame_size);
|
||||
RESTORE_STACK;
|
||||
return frame_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
int opus_projection_decoder_ctl(OpusProjectionDecoder *st, int request, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret = OPUS_OK;
|
||||
|
||||
va_start(ap, request);
|
||||
ret = opus_multistream_decoder_ctl_va_list(get_multistream_decoder(st),
|
||||
request, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void opus_projection_decoder_destroy(OpusProjectionDecoder *st)
|
||||
{
|
||||
opus_free(st);
|
||||
}
|
||||
|
||||
#endif /* ENABLE_EXPERIMENTAL_AMBISONICS */
|
412
src/opus_projection_encoder.c
Normal file
412
src/opus_projection_encoder.c
Normal file
|
@ -0,0 +1,412 @@
|
|||
/* 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 "mathops.h"
|
||||
#include "os_support.h"
|
||||
#include "opus_private.h"
|
||||
#include "opus_defines.h"
|
||||
#include "opus_projection.h"
|
||||
#include "opus_multistream.h"
|
||||
#include "stack_alloc.h"
|
||||
#include "mapping_matrix.h"
|
||||
|
||||
#ifdef ENABLE_EXPERIMENTAL_AMBISONICS
|
||||
|
||||
struct OpusProjectionEncoder
|
||||
{
|
||||
int mixing_matrix_size_in_bytes;
|
||||
int demixing_matrix_size_in_bytes;
|
||||
/* Encoder states go here */
|
||||
};
|
||||
|
||||
static int get_order_plus_one_from_channels(int channels, int *order_plus_one)
|
||||
{
|
||||
int order_plus_one_;
|
||||
int acn_channels;
|
||||
int nondiegetic_channels;
|
||||
|
||||
/* Allowed numbers of channels:
|
||||
* (1 + n)^2 + 2j, for n = 0...14 and j = 0 or 1.
|
||||
*/
|
||||
order_plus_one_ = isqrt32(channels);
|
||||
acn_channels = order_plus_one_ * order_plus_one_;
|
||||
nondiegetic_channels = channels - acn_channels;
|
||||
if (order_plus_one)
|
||||
*order_plus_one = order_plus_one_;
|
||||
|
||||
if (order_plus_one_ < 1 || order_plus_one_ > 15 ||
|
||||
(nondiegetic_channels != 0 && nondiegetic_channels != 2))
|
||||
return OPUS_BAD_ARG;
|
||||
return OPUS_OK;
|
||||
}
|
||||
|
||||
static int get_streams_from_channels(int channels, int mapping_family,
|
||||
int *streams, int *coupled_streams,
|
||||
int *order_plus_one)
|
||||
{
|
||||
if (mapping_family == 253)
|
||||
{
|
||||
if (get_order_plus_one_from_channels(channels, order_plus_one) != OPUS_OK)
|
||||
return OPUS_BAD_ARG;
|
||||
if (streams)
|
||||
*streams = (channels + 1) / 2;
|
||||
if (coupled_streams)
|
||||
*coupled_streams = channels / 2;
|
||||
return OPUS_OK;
|
||||
}
|
||||
return OPUS_BAD_ARG;
|
||||
}
|
||||
|
||||
static MappingMatrix *get_mixing_matrix(OpusProjectionEncoder *st)
|
||||
{
|
||||
return (MappingMatrix *)((char*)st + align(sizeof(OpusProjectionEncoder)));
|
||||
}
|
||||
|
||||
static MappingMatrix *get_demixing_matrix(OpusProjectionEncoder *st)
|
||||
{
|
||||
return (MappingMatrix *)((char*)st + align(sizeof(OpusProjectionEncoder) +
|
||||
st->mixing_matrix_size_in_bytes));
|
||||
}
|
||||
|
||||
static OpusMSEncoder *get_multistream_encoder(OpusProjectionEncoder *st)
|
||||
{
|
||||
return (OpusMSEncoder *)((char*)st + align(sizeof(OpusProjectionEncoder) +
|
||||
st->mixing_matrix_size_in_bytes + st->demixing_matrix_size_in_bytes));
|
||||
}
|
||||
|
||||
opus_int32 opus_projection_ambisonics_encoder_get_size(int channels,
|
||||
int mapping_family)
|
||||
{
|
||||
int nb_streams;
|
||||
int nb_coupled_streams;
|
||||
int order_plus_one;
|
||||
int matrix_rows;
|
||||
opus_int32 matrix_size;
|
||||
opus_int32 encoder_size;
|
||||
int ret;
|
||||
|
||||
ret = get_streams_from_channels(channels, mapping_family, &nb_streams,
|
||||
&nb_coupled_streams, &order_plus_one);
|
||||
if (ret != OPUS_OK)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
matrix_rows = order_plus_one * order_plus_one + 2;
|
||||
matrix_size = mapping_matrix_get_size(matrix_rows, matrix_rows);
|
||||
encoder_size =
|
||||
opus_multistream_encoder_get_size(nb_streams, nb_coupled_streams);
|
||||
if (!encoder_size)
|
||||
return 0;
|
||||
return align(sizeof(OpusProjectionEncoder) + matrix_size + matrix_size + encoder_size);
|
||||
}
|
||||
|
||||
int opus_projection_ambisonics_encoder_init(OpusProjectionEncoder *st, opus_int32 Fs,
|
||||
int channels, int mapping_family,
|
||||
int *streams, int *coupled_streams,
|
||||
int application)
|
||||
{
|
||||
MappingMatrix *mixing_matrix;
|
||||
MappingMatrix *demixing_matrix;
|
||||
OpusMSEncoder *ms_encoder;
|
||||
int nb_streams;
|
||||
int nb_coupled_streams;
|
||||
int i;
|
||||
int ret;
|
||||
unsigned char mapping[255];
|
||||
|
||||
if (get_streams_from_channels(channels, mapping_family,
|
||||
&nb_streams, &nb_coupled_streams, NULL)
|
||||
!= OPUS_OK)
|
||||
return OPUS_BAD_ARG;
|
||||
|
||||
if (streams == NULL || coupled_streams == NULL) {
|
||||
return OPUS_BAD_ARG;
|
||||
}
|
||||
*streams = nb_streams;
|
||||
*coupled_streams = nb_coupled_streams;
|
||||
|
||||
if (mapping_family == 253)
|
||||
{
|
||||
int order_plus_one;
|
||||
if (get_order_plus_one_from_channels(channels, &order_plus_one) != OPUS_OK)
|
||||
return OPUS_BAD_ARG;
|
||||
|
||||
/* Assign mixing matrix based on available pre-computed matrices. */
|
||||
mixing_matrix = get_mixing_matrix(st);
|
||||
if (order_plus_one == 2)
|
||||
{
|
||||
mapping_matrix_init(mixing_matrix, mapping_matrix_foa_mixing.rows,
|
||||
mapping_matrix_foa_mixing.cols, mapping_matrix_foa_mixing.gain,
|
||||
mapping_matrix_foa_mixing_data, 36 * sizeof(opus_int16));
|
||||
}
|
||||
else if (order_plus_one == 3)
|
||||
{
|
||||
mapping_matrix_init(mixing_matrix, mapping_matrix_soa_mixing.rows,
|
||||
mapping_matrix_soa_mixing.cols, mapping_matrix_soa_mixing.gain,
|
||||
mapping_matrix_soa_mixing_data, 121 * sizeof(opus_int16));
|
||||
}
|
||||
else if (order_plus_one == 4)
|
||||
{
|
||||
mapping_matrix_init(mixing_matrix, mapping_matrix_toa_mixing.rows,
|
||||
mapping_matrix_toa_mixing.cols, mapping_matrix_toa_mixing.gain,
|
||||
mapping_matrix_toa_mixing_data, 324 * sizeof(opus_int16));
|
||||
}
|
||||
st->mixing_matrix_size_in_bytes = mapping_matrix_get_size(
|
||||
mixing_matrix->rows, mixing_matrix->cols);
|
||||
|
||||
/* Assign demixing matrix based on available pre-computed matrices. */
|
||||
demixing_matrix = get_demixing_matrix(st);
|
||||
if (order_plus_one == 2)
|
||||
{
|
||||
mapping_matrix_init(demixing_matrix, mapping_matrix_foa_demixing.rows,
|
||||
mapping_matrix_foa_demixing.cols, mapping_matrix_foa_demixing.gain,
|
||||
mapping_matrix_foa_demixing_data, 36 * sizeof(opus_int16));
|
||||
}
|
||||
else if (order_plus_one == 3)
|
||||
{
|
||||
mapping_matrix_init(demixing_matrix, mapping_matrix_soa_demixing.rows,
|
||||
mapping_matrix_soa_demixing.cols, mapping_matrix_soa_demixing.gain,
|
||||
mapping_matrix_soa_demixing_data, 121 * sizeof(opus_int16));
|
||||
}
|
||||
else if (order_plus_one == 4)
|
||||
{
|
||||
mapping_matrix_init(demixing_matrix, mapping_matrix_toa_demixing.rows,
|
||||
mapping_matrix_toa_demixing.cols, mapping_matrix_toa_demixing.gain,
|
||||
mapping_matrix_toa_demixing_data, 324 * sizeof(opus_int16));
|
||||
}
|
||||
st->demixing_matrix_size_in_bytes = mapping_matrix_get_size(
|
||||
demixing_matrix->rows, demixing_matrix->cols);
|
||||
}
|
||||
else
|
||||
return OPUS_UNIMPLEMENTED;
|
||||
|
||||
/* Ensure matrices are large enough for desired coding scheme. */
|
||||
if (nb_streams + nb_coupled_streams > mixing_matrix->rows ||
|
||||
channels > mixing_matrix->cols ||
|
||||
channels > demixing_matrix->rows ||
|
||||
nb_streams + nb_coupled_streams > demixing_matrix->cols)
|
||||
return OPUS_BAD_ARG;
|
||||
|
||||
/* Set trivial mapping so each input channel pairs with a matrix column. */
|
||||
for (i = 0; i < channels; i++)
|
||||
{
|
||||
mapping[i] = i;
|
||||
}
|
||||
|
||||
/* Initialize multistream encoder with provided settings. */
|
||||
ms_encoder = get_multistream_encoder(st);
|
||||
ret = opus_multistream_encoder_init(ms_encoder, Fs, channels, nb_streams,
|
||||
nb_coupled_streams, mapping, application);
|
||||
return ret;
|
||||
}
|
||||
|
||||
OpusProjectionEncoder *opus_projection_ambisonics_encoder_create(
|
||||
opus_int32 Fs, int channels, int mapping_family, int *streams,
|
||||
int *coupled_streams, int application, int *error)
|
||||
{
|
||||
int size;
|
||||
int ret;
|
||||
OpusProjectionEncoder *st;
|
||||
|
||||
/* Allocate space for the projection encoder. */
|
||||
size = opus_projection_ambisonics_encoder_get_size(channels, mapping_family);
|
||||
if (!size) {
|
||||
if (error)
|
||||
*error = OPUS_ALLOC_FAIL;
|
||||
return NULL;
|
||||
}
|
||||
st = (OpusProjectionEncoder *)opus_alloc(size);
|
||||
if (!st)
|
||||
{
|
||||
if (error)
|
||||
*error = OPUS_ALLOC_FAIL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize projection encoder with provided settings. */
|
||||
ret = opus_projection_ambisonics_encoder_init(st, Fs, channels,
|
||||
mapping_family, streams, coupled_streams, application);
|
||||
if (ret != OPUS_OK)
|
||||
{
|
||||
opus_free(st);
|
||||
st = NULL;
|
||||
}
|
||||
if (error)
|
||||
*error = ret;
|
||||
return st;
|
||||
}
|
||||
|
||||
int opus_projection_encode(OpusProjectionEncoder *st, const opus_int16 *pcm,
|
||||
int frame_size, unsigned char *data,
|
||||
opus_int32 max_data_bytes)
|
||||
{
|
||||
#ifdef NONTHREADSAFE_PSEUDOSTACK
|
||||
celt_fatal("Unable to use opus_projection_encode() when NONTHREADSAFE_PSEUDOSTACK is defined.");
|
||||
#endif
|
||||
MappingMatrix *matrix;
|
||||
OpusMSEncoder *ms_encoder;
|
||||
int ret;
|
||||
VARDECL(opus_int16, buf);
|
||||
ALLOC_STACK;
|
||||
|
||||
matrix = get_mixing_matrix(st);
|
||||
ms_encoder = get_multistream_encoder(st);
|
||||
ALLOC(buf, (ms_encoder->layout.nb_streams + ms_encoder->layout.nb_coupled_streams) *
|
||||
frame_size, opus_int16);
|
||||
mapping_matrix_multiply_short(matrix, pcm,
|
||||
ms_encoder->layout.nb_channels, buf,
|
||||
ms_encoder->layout.nb_streams + ms_encoder->layout.nb_coupled_streams,
|
||||
frame_size);
|
||||
ret = opus_multistream_encode(ms_encoder, buf, frame_size, data, max_data_bytes);
|
||||
RESTORE_STACK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_FLOAT_API
|
||||
int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm,
|
||||
int frame_size, unsigned char *data,
|
||||
opus_int32 max_data_bytes)
|
||||
{
|
||||
#ifdef NONTHREADSAFE_PSEUDOSTACK
|
||||
celt_fatal("Unable to use opus_projection_encode_float() when NONTHREADSAFE_PSEUDOSTACK is defined.");
|
||||
#endif
|
||||
MappingMatrix *matrix;
|
||||
OpusMSEncoder *ms_encoder;
|
||||
int ret;
|
||||
VARDECL(float, buf);
|
||||
ALLOC_STACK;
|
||||
|
||||
matrix = get_mixing_matrix(st);
|
||||
ms_encoder = get_multistream_encoder(st);
|
||||
ALLOC(buf, (ms_encoder->layout.nb_streams + ms_encoder->layout.nb_coupled_streams) *
|
||||
frame_size, float);
|
||||
mapping_matrix_multiply_float(matrix, pcm,
|
||||
ms_encoder->layout.nb_channels, buf,
|
||||
ms_encoder->layout.nb_streams + ms_encoder->layout.nb_coupled_streams,
|
||||
frame_size);
|
||||
ret = opus_multistream_encode_float(ms_encoder, buf, frame_size, data, max_data_bytes);
|
||||
RESTORE_STACK;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
void opus_projection_encoder_destroy(OpusProjectionEncoder *st)
|
||||
{
|
||||
opus_free(st);
|
||||
}
|
||||
|
||||
int opus_projection_encoder_ctl(OpusProjectionEncoder *st, int request, ...)
|
||||
{
|
||||
MappingMatrix *demixing_matrix;
|
||||
OpusMSEncoder *ms_encoder;
|
||||
int ret = OPUS_OK;
|
||||
|
||||
ms_encoder = get_multistream_encoder(st);
|
||||
demixing_matrix = get_demixing_matrix(st);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, request);
|
||||
switch(request)
|
||||
{
|
||||
case OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST:
|
||||
{
|
||||
opus_int32 *value = va_arg(ap, opus_int32*);
|
||||
if (!value)
|
||||
{
|
||||
goto bad_arg;
|
||||
}
|
||||
*value =
|
||||
ms_encoder->layout.nb_channels * (ms_encoder->layout.nb_streams
|
||||
+ ms_encoder->layout.nb_coupled_streams) * sizeof(opus_int16);
|
||||
}
|
||||
break;
|
||||
case OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN_REQUEST:
|
||||
{
|
||||
opus_int32 *value = va_arg(ap, opus_int32*);
|
||||
if (!value)
|
||||
{
|
||||
goto bad_arg;
|
||||
}
|
||||
*value = demixing_matrix->gain;
|
||||
}
|
||||
break;
|
||||
case OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST:
|
||||
{
|
||||
int i;
|
||||
int nb_input_streams;
|
||||
int nb_output_streams;
|
||||
unsigned char *external_char;
|
||||
opus_int16 *internal_short;
|
||||
opus_int32 external_size;
|
||||
opus_int32 internal_size;
|
||||
|
||||
/* (I/O is in relation to the decoder's perspective). */
|
||||
nb_input_streams = ms_encoder->layout.nb_streams +
|
||||
ms_encoder->layout.nb_coupled_streams;
|
||||
nb_output_streams = ms_encoder->layout.nb_channels;
|
||||
|
||||
external_char = va_arg(ap, unsigned char *);
|
||||
external_size = va_arg(ap, opus_uint32);
|
||||
if (!external_char)
|
||||
{
|
||||
goto bad_arg;
|
||||
}
|
||||
internal_short = mapping_matrix_get_data(demixing_matrix);
|
||||
internal_size = nb_input_streams * nb_output_streams * sizeof(opus_int16);
|
||||
if (external_size != internal_size)
|
||||
{
|
||||
goto bad_arg;
|
||||
}
|
||||
|
||||
/* Copy demixing matrix subset to output destination. */
|
||||
for (i = 0; i < nb_input_streams * nb_output_streams; i++)
|
||||
{
|
||||
external_char[2*i] = (unsigned char)internal_short[i];
|
||||
external_char[2*i+1] = (unsigned char)(internal_short[i] >> 8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
ret = opus_multistream_encoder_ctl_va_list(ms_encoder, request, ap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
va_end(ap);
|
||||
return ret;
|
||||
|
||||
bad_arg:
|
||||
va_end(ap);
|
||||
return OPUS_BAD_ARG;
|
||||
}
|
||||
|
||||
#endif /* ENABLE_EXPERIMENTAL_AMBISONICS */
|
431
tests/test_opus_projection.c
Normal file
431
tests/test_opus_projection.c
Normal file
|
@ -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 */
|
Loading…
Add table
Add a link
Reference in a new issue