123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- /* 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"
- struct OpusProjectionEncoder
- {
- opus_int32 mixing_matrix_size_in_bytes;
- opus_int32 demixing_matrix_size_in_bytes;
- /* Encoder states go here */
- };
- #if !defined(DISABLE_FLOAT_API)
- static void opus_projection_copy_channel_in_float(
- opus_val16 *dst,
- int dst_stride,
- const void *src,
- int src_stride,
- int src_channel,
- int frame_size,
- void *user_data
- )
- {
- mapping_matrix_multiply_channel_in_float((const MappingMatrix*)user_data,
- (const float*)src, src_stride, dst, src_channel, dst_stride, frame_size);
- }
- #endif
- static void opus_projection_copy_channel_in_short(
- opus_val16 *dst,
- int dst_stride,
- const void *src,
- int src_stride,
- int src_channel,
- int frame_size,
- void *user_data
- )
- {
- mapping_matrix_multiply_channel_in_short((const MappingMatrix*)user_data,
- (const opus_int16*)src, src_stride, dst, src_channel, dst_stride, frame_size);
- }
- 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.
- */
- if (channels < 1 || channels > 227)
- return OPUS_BAD_ARG;
- order_plus_one_ = isqrt32(channels);
- acn_channels = order_plus_one_ * order_plus_one_;
- nondiegetic_channels = channels - acn_channels;
- if (nondiegetic_channels != 0 && nondiegetic_channels != 2)
- return OPUS_BAD_ARG;
- if (order_plus_one)
- *order_plus_one = order_plus_one_;
- 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 == 3)
- {
- 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)
- {
- /* void* cast avoids clang -Wcast-align warning */
- return (MappingMatrix *)(void*)((char*)st +
- align(sizeof(OpusProjectionEncoder)));
- }
- static MappingMatrix *get_enc_demixing_matrix(OpusProjectionEncoder *st)
- {
- /* void* cast avoids clang -Wcast-align warning */
- return (MappingMatrix *)(void*)((char*)st +
- align(sizeof(OpusProjectionEncoder) +
- st->mixing_matrix_size_in_bytes));
- }
- static OpusMSEncoder *get_multistream_encoder(OpusProjectionEncoder *st)
- {
- /* void* cast avoids clang -Wcast-align warning */
- return (OpusMSEncoder *)(void*)((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 mixing_matrix_rows, mixing_matrix_cols;
- int demixing_matrix_rows, demixing_matrix_cols;
- opus_int32 mixing_matrix_size, demixing_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;
- if (order_plus_one == 2)
- {
- mixing_matrix_rows = mapping_matrix_foa_mixing.rows;
- mixing_matrix_cols = mapping_matrix_foa_mixing.cols;
- demixing_matrix_rows = mapping_matrix_foa_demixing.rows;
- demixing_matrix_cols = mapping_matrix_foa_demixing.cols;
- }
- else if (order_plus_one == 3)
- {
- mixing_matrix_rows = mapping_matrix_soa_mixing.rows;
- mixing_matrix_cols = mapping_matrix_soa_mixing.cols;
- demixing_matrix_rows = mapping_matrix_soa_demixing.rows;
- demixing_matrix_cols = mapping_matrix_soa_demixing.cols;
- }
- else if (order_plus_one == 4)
- {
- mixing_matrix_rows = mapping_matrix_toa_mixing.rows;
- mixing_matrix_cols = mapping_matrix_toa_mixing.cols;
- demixing_matrix_rows = mapping_matrix_toa_demixing.rows;
- demixing_matrix_cols = mapping_matrix_toa_demixing.cols;
- }
- else
- return 0;
- mixing_matrix_size =
- mapping_matrix_get_size(mixing_matrix_rows, mixing_matrix_cols);
- if (!mixing_matrix_size)
- return 0;
- demixing_matrix_size =
- mapping_matrix_get_size(demixing_matrix_rows, demixing_matrix_cols);
- if (!demixing_matrix_size)
- return 0;
- encoder_size =
- opus_multistream_encoder_get_size(nb_streams, nb_coupled_streams);
- if (!encoder_size)
- return 0;
- return align(sizeof(OpusProjectionEncoder)) +
- mixing_matrix_size + demixing_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 i;
- int ret;
- int order_plus_one;
- unsigned char mapping[255];
- if (streams == NULL || coupled_streams == NULL) {
- return OPUS_BAD_ARG;
- }
- if (get_streams_from_channels(channels, mapping_family, streams,
- coupled_streams, &order_plus_one) != OPUS_OK)
- return OPUS_BAD_ARG;
- if (mapping_family == 3)
- {
- /* 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,
- sizeof(mapping_matrix_foa_mixing_data));
- }
- 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,
- sizeof(mapping_matrix_soa_mixing_data));
- }
- 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,
- sizeof(mapping_matrix_toa_mixing_data));
- }
- else
- return OPUS_BAD_ARG;
- st->mixing_matrix_size_in_bytes = mapping_matrix_get_size(
- mixing_matrix->rows, mixing_matrix->cols);
- if (!st->mixing_matrix_size_in_bytes)
- return OPUS_BAD_ARG;
- /* Assign demixing matrix based on available pre-computed matrices. */
- demixing_matrix = get_enc_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,
- sizeof(mapping_matrix_foa_demixing_data));
- }
- 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,
- sizeof(mapping_matrix_soa_demixing_data));
- }
- 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,
- sizeof(mapping_matrix_toa_demixing_data));
- }
- else
- return OPUS_BAD_ARG;
- st->demixing_matrix_size_in_bytes = mapping_matrix_get_size(
- demixing_matrix->rows, demixing_matrix->cols);
- if (!st->demixing_matrix_size_in_bytes)
- return OPUS_BAD_ARG;
- }
- else
- return OPUS_UNIMPLEMENTED;
- /* Ensure matrices are large enough for desired coding scheme. */
- if (*streams + *coupled_streams > mixing_matrix->rows ||
- channels > mixing_matrix->cols ||
- channels > demixing_matrix->rows ||
- *streams + *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, *streams,
- *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)
- {
- return opus_multistream_encode_native(get_multistream_encoder(st),
- opus_projection_copy_channel_in_short, pcm, frame_size, data,
- max_data_bytes, 16, downmix_int, 0, get_mixing_matrix(st));
- }
- #ifndef DISABLE_FLOAT_API
- #ifdef FIXED_POINT
- int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm,
- int frame_size, unsigned char *data,
- opus_int32 max_data_bytes)
- {
- return opus_multistream_encode_native(get_multistream_encoder(st),
- opus_projection_copy_channel_in_float, pcm, frame_size, data,
- max_data_bytes, 16, downmix_float, 1, get_mixing_matrix(st));
- }
- #else
- int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm,
- int frame_size, unsigned char *data,
- opus_int32 max_data_bytes)
- {
- return opus_multistream_encode_native(get_multistream_encoder(st),
- opus_projection_copy_channel_in_float, pcm, frame_size, data,
- max_data_bytes, 24, downmix_float, 1, get_mixing_matrix(st));
- }
- #endif
- #endif
- void opus_projection_encoder_destroy(OpusProjectionEncoder *st)
- {
- opus_free(st);
- }
- int opus_projection_encoder_ctl(OpusProjectionEncoder *st, int request, ...)
- {
- va_list ap;
- MappingMatrix *demixing_matrix;
- OpusMSEncoder *ms_encoder;
- int ret = OPUS_OK;
- ms_encoder = get_multistream_encoder(st);
- demixing_matrix = get_enc_demixing_matrix(st);
- 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, j, k, l;
- 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_int32);
- 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. */
- l = 0;
- for (i = 0; i < nb_input_streams; i++) {
- for (j = 0; j < nb_output_streams; j++) {
- k = demixing_matrix->rows * i + j;
- external_char[2*l] = (unsigned char)internal_short[k];
- external_char[2*l+1] = (unsigned char)(internal_short[k] >> 8);
- l++;
- }
- }
- }
- 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;
- }
|