From d0235b29e78cfe46bc9bf825231d4f84467e705b Mon Sep 17 00:00:00 2001 From: Jean-Marc Valin Date: Thu, 13 Jun 2024 10:55:12 -0400 Subject: [PATCH] Adds 24-bit API for projection encoder/decoder --- include/opus_projection.h | 75 +++++++++++++++++++++++++++++++++++ src/mapping_matrix.c | 66 ++++++++++++++++++++++++++++++ src/mapping_matrix.h | 20 ++++++++++ src/opus_projection_decoder.c | 30 ++++++++++++++ src/opus_projection_encoder.c | 23 +++++++++++ 5 files changed, 214 insertions(+) diff --git a/include/opus_projection.h b/include/opus_projection.h index 5bdc6e66d..76c75e564 100644 --- a/include/opus_projection.h +++ b/include/opus_projection.h @@ -260,6 +260,44 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_encode( opus_int32 max_data_bytes ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4); +/** Encodes a projection Opus frame. + * @param st OpusProjectionEncoder*: Projection encoder state. + * @param[in] pcm const opus_int32*: The input signal as interleaved + * samples representing (or slightly exceeding) 24-bit values. + * This must contain + * frame_size*channels + * samples. + * @param frame_size int: 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 unsigned char*: Output payload. + * This must contain storage for at + * least \a max_data_bytes. + * @param [in] max_data_bytes opus_int32: 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_encode24( + OpusProjectionEncoder *st, + const opus_int32 *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 OpusProjectionEncoder*: Projection encoder state. @@ -493,6 +531,43 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_decode( int decode_fec ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4); +/** Decode a projection Opus packet. + * @param st OpusProjectionDecoder*: Projection decoder state. + * @param[in] data const unsigned char*: Input payload. + * Use a NULL + * pointer to indicate packet + * loss. + * @param len opus_int32: Number of bytes in payload. + * @param[out] pcm opus_int32*: Output signal, with interleaved + * samples representing (or slightly exceeding) 24-bit values. + * This must contain room for + * frame_size*channels + * samples. + * @param frame_size int: 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 + * must be a multiple of 2.5 ms. + * @param decode_fec int: 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_decode24( + OpusProjectionDecoder *st, + const unsigned char *data, + opus_int32 len, + opus_int32 *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 OpusProjectionDecoder*: Projection decoder state. diff --git a/src/mapping_matrix.c b/src/mapping_matrix.c index 72b759954..983b19e9e 100644 --- a/src/mapping_matrix.c +++ b/src/mapping_matrix.c @@ -216,6 +216,72 @@ void mapping_matrix_multiply_channel_out_short( } } +void mapping_matrix_multiply_channel_in_int24( + const MappingMatrix *matrix, + const opus_int32 *input, + int input_rows, + opus_res *output, + int output_row, + int output_rows, + int frame_size) +{ + /* Matrix data is ordered col-wise. */ + opus_int16* matrix_data; + int i, 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++) + { + opus_val64 tmp = 0; + for (col = 0; col < input_rows; col++) + { + tmp += + matrix_data[MATRIX_INDEX(matrix->rows, output_row, col)] * + (opus_val64)input[MATRIX_INDEX(input_rows, col, i)]; + } +#if defined(FIXED_POINT) + output[output_rows * i] = INT24TORES((tmp + 16384) >> 15); +#else + output[output_rows * i] = INT24TORES((1/(32768.f))*tmp); +#endif + } +} + +void mapping_matrix_multiply_channel_out_int24( + const MappingMatrix *matrix, + const opus_res *input, + int input_row, + int input_rows, + opus_int32 *output, + int output_rows, + int frame_size) +{ + /* Matrix data is ordered col-wise. */ + opus_int16* matrix_data; + int i, row; + opus_int32 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++) + { + input_sample = RES2INT24(input[input_rows * i]); + for (row = 0; row < output_rows; row++) + { + opus_int64 tmp = + (opus_int64)matrix_data[MATRIX_INDEX(matrix->rows, row, input_row)] * + input_sample; + output[MATRIX_INDEX(output_rows, row, i)] += (tmp + 16384) >> 15; + } + } +} + + 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, diff --git a/src/mapping_matrix.h b/src/mapping_matrix.h index 0b9ef8197..b9458849e 100644 --- a/src/mapping_matrix.h +++ b/src/mapping_matrix.h @@ -103,6 +103,26 @@ void mapping_matrix_multiply_channel_out_short( int frame_size ); + +void mapping_matrix_multiply_channel_in_int24( + const MappingMatrix *matrix, + const opus_int32 *input, + int input_rows, + opus_res *output, + int output_row, + int output_rows, + int frame_size +); + +void mapping_matrix_multiply_channel_out_int24( + const MappingMatrix *matrix, + const opus_res *input, + int input_row, + int input_rows, + opus_int32 *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 diff --git a/src/opus_projection_decoder.c b/src/opus_projection_decoder.c index ebd463d27..afdc5e9b3 100644 --- a/src/opus_projection_decoder.c +++ b/src/opus_projection_decoder.c @@ -89,6 +89,27 @@ static void opus_projection_copy_channel_out_short( src_stride, short_dst, dst_stride, frame_size); } +static void opus_projection_copy_channel_out_int24( + void *dst, + int dst_stride, + int dst_channel, + const opus_res *src, + int src_stride, + int frame_size, + void *user_data) +{ + opus_int32 *short_dst; + const MappingMatrix *matrix; + short_dst = (opus_int32 *)dst; + matrix = (const MappingMatrix *)user_data; + if (dst_channel == 0) + OPUS_CLEAR(short_dst, frame_size * dst_stride); + + if (src != NULL) + mapping_matrix_multiply_channel_out_int24(matrix, src, dst_channel, + src_stride, short_dst, dst_stride, frame_size); +} + static MappingMatrix *get_dec_demixing_matrix(OpusProjectionDecoder *st) { /* void* cast avoids clang -Wcast-align warning */ @@ -224,6 +245,15 @@ int opus_projection_decode(OpusProjectionDecoder *st, const unsigned char *data, get_dec_demixing_matrix(st)); } +int opus_projection_decode24(OpusProjectionDecoder *st, const unsigned char *data, + opus_int32 len, opus_int32 *pcm, int frame_size, + int decode_fec) +{ + return opus_multistream_decode_native(get_multistream_decoder(st), data, len, + pcm, opus_projection_copy_channel_out_int24, frame_size, decode_fec, 0, + get_dec_demixing_matrix(st)); +} + #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) diff --git a/src/opus_projection_encoder.c b/src/opus_projection_encoder.c index cecabb4f8..d4ac9bbe4 100644 --- a/src/opus_projection_encoder.c +++ b/src/opus_projection_encoder.c @@ -75,6 +75,20 @@ static void opus_projection_copy_channel_in_short( (const opus_int16*)src, src_stride, dst, src_channel, dst_stride, frame_size); } +static void opus_projection_copy_channel_in_int24( + opus_res *dst, + int dst_stride, + const void *src, + int src_stride, + int src_channel, + int frame_size, + void *user_data +) +{ + mapping_matrix_multiply_channel_in_int24((const MappingMatrix*)user_data, + (const opus_int32*)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_; @@ -392,6 +406,15 @@ int opus_projection_encode(OpusProjectionEncoder *st, const opus_int16 *pcm, max_data_bytes, 16, downmix_int, 0, get_mixing_matrix(st)); } +int opus_projection_encode24(OpusProjectionEncoder *st, const opus_int32 *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_int24, pcm, frame_size, data, + max_data_bytes, MAX_ENCODING_DEPTH, downmix_int, 0, get_mixing_matrix(st)); +} + #ifndef DISABLE_FLOAT_API int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm, int frame_size, unsigned char *data,