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,