Fix memory issues in Projection API.

Modified by Jean-Marc Valin

Signed-off-by: Jean-Marc Valin <jmvalin@jmvalin.ca>
This commit is contained in:
Andrew Allen 2017-12-04 15:32:18 -08:00 committed by Jean-Marc Valin
parent eee6898242
commit 65f11d326d
No known key found for this signature in database
GPG key ID: 5E5DD9A36F9189C8
8 changed files with 456 additions and 330 deletions

View file

@ -41,12 +41,12 @@
int mapping_matrix_get_size(int rows, int cols)
{
return align(sizeof(MappingMatrix)) + rows * cols * sizeof(opus_int16);
return align(sizeof(MappingMatrix)) + align(rows * cols * sizeof(opus_int16));
}
opus_int16 *mapping_matrix_get_data(const MappingMatrix *matrix)
{
return (opus_int16*)(void *)(matrix + align(sizeof(MappingMatrix)));
return (opus_int16*)((char*)matrix + align(sizeof(MappingMatrix)));
}
void mapping_matrix_init(MappingMatrix * const matrix,
@ -58,7 +58,7 @@ void mapping_matrix_init(MappingMatrix * const matrix,
#if !defined(ENABLE_ASSERTIONS)
(void)data_size;
#endif
celt_assert((opus_uint32)data_size == rows * cols * sizeof(opus_int16));
celt_assert(align(data_size) == align(rows * cols * sizeof(opus_int16)));
matrix->rows = rows;
matrix->cols = cols;
@ -71,18 +71,18 @@ void mapping_matrix_init(MappingMatrix * const matrix,
}
#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)
void mapping_matrix_multiply_channel_in_float(
const MappingMatrix *matrix,
const float *input,
int input_rows,
opus_val16 *output,
int output_row,
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
*/
/* Matrix data is ordered col-wise. */
opus_int16* matrix_data;
int i, row, col;
float matrix_cell, input_sample;
int i, col;
celt_assert(input_rows <= matrix->cols && output_rows <= matrix->rows);
@ -90,31 +90,70 @@ void mapping_matrix_multiply_float(const MappingMatrix *matrix,
for (i = 0; i < frame_size; i++)
{
float tmp = 0;
for (col = 0; col < input_rows; col++)
{
tmp +=
matrix_data[MATRIX_INDEX(matrix->rows, output_row, col)] *
input[MATRIX_INDEX(input_rows, col, i)];
}
#if defined(FIXED_POINT)
output[output_rows * i] = FLOAT2INT16((1/32768.f)*tmp);
#else
output[output_rows * i] = (1/32768.f)*tmp;
#endif
}
}
void mapping_matrix_multiply_channel_out_float(
const MappingMatrix *matrix,
const opus_val16 *input,
int input_row,
int input_rows,
float *output,
int output_rows,
int frame_size
)
{
/* Matrix data is ordered col-wise. */
opus_int16* matrix_data;
int i, row;
float 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++)
{
#if defined(FIXED_POINT)
input_sample = (1/32768.f)*input[input_rows * i];
#else
input_sample = input[input_rows * i];
#endif
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;
}
float tmp =
(1/32768.f)*matrix_data[MATRIX_INDEX(matrix->rows, row, input_row)] *
input_sample;
output[MATRIX_INDEX(output_rows, row, i)] += tmp;
}
}
}
#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)
void mapping_matrix_multiply_channel_in_short(
const MappingMatrix *matrix,
const opus_int16 *input,
int input_rows,
opus_val16 *output,
int output_row,
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
*/
/* Matrix data is ordered col-wise. */
opus_int16* matrix_data;
int i, row, col;
int i, col;
celt_assert(input_rows <= matrix->cols && output_rows <= matrix->rows);
@ -122,16 +161,58 @@ void mapping_matrix_multiply_short(const MappingMatrix *matrix,
for (i = 0; i < frame_size; i++)
{
opus_val32 tmp = 0;
for (col = 0; col < input_rows; col++)
{
#if defined(FIXED_POINT)
tmp +=
((opus_int32)matrix_data[MATRIX_INDEX(matrix->rows, output_row, col)] *
(opus_int32)input[MATRIX_INDEX(input_rows, col, i)]) >> 8;
#else
tmp +=
matrix_data[MATRIX_INDEX(matrix->rows, output_row, col)] *
input[MATRIX_INDEX(input_rows, col, i)];
#endif
}
#if defined(FIXED_POINT)
output[output_rows * i] = (opus_int16)((tmp + 64) >> 7);
#else
output[output_rows * i] = (1/(32768.f*32768.f))*tmp;
#endif
}
}
void mapping_matrix_multiply_channel_out_short(
const MappingMatrix *matrix,
const opus_val16 *input,
int input_row,
int input_rows,
opus_int16 *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++)
{
#if defined(FIXED_POINT)
input_sample = (opus_int32)input[input_rows * i];
#else
input_sample = (opus_int32)FLOAT2INT16(input[input_rows * i]);
#endif
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;
opus_int32 tmp =
(opus_int32)matrix_data[MATRIX_INDEX(matrix->rows, row, input_row)] *
input_sample;
output[MATRIX_INDEX(output_rows, row, i)] += (tmp + 16384) >> 15;
}
}
}

View file

@ -44,8 +44,8 @@ extern "C" {
typedef struct MappingMatrix
{
int rows;
int cols;
int rows; /* number of channels outputted from matrix. */
int cols; /* number of channels inputted to matrix. */
int gain; /* in dB. S7.8-format. */
/* Matrix cell data goes here using col-wise ordering. */
} MappingMatrix;
@ -64,20 +64,42 @@ void mapping_matrix_init(
);
#ifndef DISABLE_FLOAT_API
void mapping_matrix_multiply_float(
void mapping_matrix_multiply_channel_in_float(
const MappingMatrix *matrix,
const float *input,
int input_rows,
opus_val16 *output,
int output_row,
int output_rows,
int frame_size
);
void mapping_matrix_multiply_channel_out_float(
const MappingMatrix *matrix,
const opus_val16 *input,
int input_row,
int input_rows,
float *output,
int output_rows,
int frame_size
);
#endif /* DISABLE_FLOAT_API */
void mapping_matrix_multiply_short(
void mapping_matrix_multiply_channel_in_short(
const MappingMatrix *matrix,
const opus_int16 *input,
int input_rows,
opus_val16 *output,
int output_row,
int output_rows,
int frame_size
);
void mapping_matrix_multiply_channel_out_short(
const MappingMatrix *matrix,
const opus_val16 *input,
int input_row,
int input_rows,
opus_int16 *output,
int output_rows,
int frame_size

View file

@ -135,15 +135,6 @@ OpusMSDecoder *opus_multistream_decoder_create(
return st;
}
typedef void (*opus_copy_channel_out_func)(
void *dst,
int dst_stride,
int dst_channel,
const opus_val16 *src,
int src_stride,
int frame_size
);
static int opus_multistream_packet_validate(const unsigned char *data,
opus_int32 len, int nb_streams, opus_int32 Fs)
{
@ -173,7 +164,7 @@ static int opus_multistream_packet_validate(const unsigned char *data,
return samples;
}
static int opus_multistream_decode_native(
int opus_multistream_decode_native(
OpusMSDecoder *st,
const unsigned char *data,
opus_int32 len,
@ -181,7 +172,8 @@ static int opus_multistream_decode_native(
opus_copy_channel_out_func copy_channel_out,
int frame_size,
int decode_fec,
int soft_clip
int soft_clip,
void *user_data
)
{
opus_int32 Fs;
@ -258,7 +250,7 @@ static int opus_multistream_decode_native(
while ( (chan = get_left_channel(&st->layout, s, prev)) != -1)
{
(*copy_channel_out)(pcm, st->layout.nb_channels, chan,
buf, 2, frame_size);
buf, 2, frame_size, user_data);
prev = chan;
}
prev = -1;
@ -266,7 +258,7 @@ static int opus_multistream_decode_native(
while ( (chan = get_right_channel(&st->layout, s, prev)) != -1)
{
(*copy_channel_out)(pcm, st->layout.nb_channels, chan,
buf+1, 2, frame_size);
buf+1, 2, frame_size, user_data);
prev = chan;
}
} else {
@ -276,7 +268,7 @@ static int opus_multistream_decode_native(
while ( (chan = get_mono_channel(&st->layout, s, prev)) != -1)
{
(*copy_channel_out)(pcm, st->layout.nb_channels, chan,
buf, 1, frame_size);
buf, 1, frame_size, user_data);
prev = chan;
}
}
@ -287,7 +279,7 @@ static int opus_multistream_decode_native(
if (st->layout.mapping[c] == 255)
{
(*copy_channel_out)(pcm, st->layout.nb_channels, c,
NULL, 0, frame_size);
NULL, 0, frame_size, user_data);
}
}
RESTORE_STACK;
@ -301,11 +293,13 @@ static void opus_copy_channel_out_float(
int dst_channel,
const opus_val16 *src,
int src_stride,
int frame_size
int frame_size,
void *user_data
)
{
float *float_dst;
opus_int32 i;
(void)user_data;
float_dst = (float*)dst;
if (src != NULL)
{
@ -330,11 +324,13 @@ static void opus_copy_channel_out_short(
int dst_channel,
const opus_val16 *src,
int src_stride,
int frame_size
int frame_size,
void *user_data
)
{
opus_int16 *short_dst;
opus_int32 i;
(void)user_data;
short_dst = (opus_int16*)dst;
if (src != NULL)
{
@ -365,7 +361,7 @@ int opus_multistream_decode(
)
{
return opus_multistream_decode_native(st, data, len,
pcm, opus_copy_channel_out_short, frame_size, decode_fec, 0);
pcm, opus_copy_channel_out_short, frame_size, decode_fec, 0, NULL);
}
#ifndef DISABLE_FLOAT_API
@ -373,7 +369,7 @@ int opus_multistream_decode_float(OpusMSDecoder *st, const unsigned char *data,
opus_int32 len, float *pcm, int frame_size, int decode_fec)
{
return opus_multistream_decode_native(st, data, len,
pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0);
pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0, NULL);
}
#endif
@ -383,20 +379,20 @@ int opus_multistream_decode(OpusMSDecoder *st, const unsigned char *data,
opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec)
{
return opus_multistream_decode_native(st, data, len,
pcm, opus_copy_channel_out_short, frame_size, decode_fec, 1);
pcm, opus_copy_channel_out_short, frame_size, decode_fec, 1, NULL);
}
int opus_multistream_decode_float(
OpusMSDecoder *st,
const unsigned char *data,
opus_int32 len,
float *pcm,
opus_val16 *pcm,
int frame_size,
int decode_fec
)
{
return opus_multistream_decode_native(st, data, len,
pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0);
pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0, NULL);
}
#endif

View file

@ -61,15 +61,6 @@ static const VorbisLayout vorbis_mappings[8] = {
{5, 3, {0, 6, 1, 2, 3, 4, 5, 7}}, /* 8: 7.1 surround */
};
typedef void (*opus_copy_channel_in_func)(
opus_val16 *dst,
int dst_stride,
const void *src,
int src_stride,
int src_channel,
int frame_size
);
static opus_val32 *ms_get_preemph_mem(OpusMSEncoder *st)
{
int s;
@ -274,7 +265,7 @@ void surround_analysis(const CELTMode *celt_mode, const void *pcm, opus_val16 *b
int nb_frames = frame_size/freq_size;
celt_assert(nb_frames*freq_size == frame_size);
OPUS_COPY(in, mem+c*overlap, overlap);
(*copy_channel_in)(x, 1, pcm, channels, c, len);
(*copy_channel_in)(x, 1, pcm, channels, c, len, NULL);
celt_preemphasis(x, in+overlap, frame_size, 1, upsample, celt_mode->preemph, preemph_mem+c, 0);
#ifndef FIXED_POINT
{
@ -862,7 +853,7 @@ static opus_int32 rate_allocation(
/* Max size in case the encoder decides to return six frames (6 x 20 ms = 120 ms) */
#define MS_FRAME_TMP (6*1275+12)
static int opus_multistream_encode_native
int opus_multistream_encode_native
(
OpusMSEncoder *st,
opus_copy_channel_in_func copy_channel_in,
@ -872,7 +863,8 @@ static int opus_multistream_encode_native
opus_int32 max_data_bytes,
int lsb_depth,
downmix_func downmix,
int float_api
int float_api,
void *user_data
)
{
opus_int32 Fs;
@ -1006,9 +998,9 @@ static int opus_multistream_encode_native
left = get_left_channel(&st->layout, s, -1);
right = get_right_channel(&st->layout, s, -1);
(*copy_channel_in)(buf, 2,
pcm, st->layout.nb_channels, left, frame_size);
pcm, st->layout.nb_channels, left, frame_size, user_data);
(*copy_channel_in)(buf+1, 2,
pcm, st->layout.nb_channels, right, frame_size);
pcm, st->layout.nb_channels, right, frame_size, user_data);
ptr += align(coupled_size);
if (st->mapping_type == MAPPING_TYPE_SURROUND)
{
@ -1024,7 +1016,7 @@ static int opus_multistream_encode_native
int i;
int chan = get_mono_channel(&st->layout, s, -1);
(*copy_channel_in)(buf, 1,
pcm, st->layout.nb_channels, chan, frame_size);
pcm, st->layout.nb_channels, chan, frame_size, user_data);
ptr += align(mono_size);
if (st->mapping_type == MAPPING_TYPE_SURROUND)
{
@ -1083,11 +1075,13 @@ static void opus_copy_channel_in_float(
const void *src,
int src_stride,
int src_channel,
int frame_size
int frame_size,
void *user_data
)
{
const float *float_src;
opus_int32 i;
(void)user_data;
float_src = (const float *)src;
for (i=0;i<frame_size;i++)
#if defined(FIXED_POINT)
@ -1104,11 +1098,13 @@ static void opus_copy_channel_in_short(
const void *src,
int src_stride,
int src_channel,
int frame_size
int frame_size,
void *user_data
)
{
const opus_int16 *short_src;
opus_int32 i;
(void)user_data;
short_src = (const opus_int16 *)src;
for (i=0;i<frame_size;i++)
#if defined(FIXED_POINT)
@ -1129,7 +1125,7 @@ int opus_multistream_encode(
)
{
return opus_multistream_encode_native(st, opus_copy_channel_in_short,
pcm, frame_size, data, max_data_bytes, 16, downmix_int, 0);
pcm, frame_size, data, max_data_bytes, 16, downmix_int, 0, NULL);
}
#ifndef DISABLE_FLOAT_API
@ -1142,7 +1138,7 @@ int opus_multistream_encode_float(
)
{
return opus_multistream_encode_native(st, opus_copy_channel_in_float,
pcm, frame_size, data, max_data_bytes, 16, downmix_float, 1);
pcm, frame_size, data, max_data_bytes, 16, downmix_float, 1, NULL);
}
#endif
@ -1158,7 +1154,7 @@ int opus_multistream_encode_float
)
{
return opus_multistream_encode_native(st, opus_copy_channel_in_float,
pcm, frame_size, data, max_data_bytes, 24, downmix_float, 1);
pcm, frame_size, data, max_data_bytes, 24, downmix_float, 1, NULL);
}
int opus_multistream_encode(
@ -1170,7 +1166,7 @@ int opus_multistream_encode(
)
{
return opus_multistream_encode_native(st, opus_copy_channel_in_short,
pcm, frame_size, data, max_data_bytes, 16, downmix_int, 0);
pcm, frame_size, data, max_data_bytes, 16, downmix_int, 0, NULL);
}
#endif

View file

@ -88,7 +88,25 @@ int get_left_channel(const ChannelLayout *layout, int stream_id, int prev);
int get_right_channel(const ChannelLayout *layout, int stream_id, int prev);
int get_mono_channel(const ChannelLayout *layout, int stream_id, int prev);
typedef void (*opus_copy_channel_in_func)(
opus_val16 *dst,
int dst_stride,
const void *src,
int src_stride,
int src_channel,
int frame_size,
void *user_data
);
typedef void (*opus_copy_channel_out_func)(
void *dst,
int dst_stride,
int dst_channel,
const opus_val16 *src,
int src_stride,
int frame_size,
void *user_data
);
#define MODE_SILK_ONLY 1000
#define MODE_HYBRID 1001
@ -156,4 +174,30 @@ opus_int32 opus_repacketizer_out_range_impl(OpusRepacketizer *rp, int begin, int
int pad_frame(unsigned char *data, opus_int32 len, opus_int32 new_len);
int opus_multistream_encode_native
(
struct OpusMSEncoder *st,
opus_copy_channel_in_func copy_channel_in,
const void *pcm,
int analysis_frame_size,
unsigned char *data,
opus_int32 max_data_bytes,
int lsb_depth,
downmix_func downmix,
int float_api,
void *user_data
);
int opus_multistream_decode_native(
struct OpusMSDecoder *st,
const unsigned char *data,
opus_int32 len,
void *pcm,
opus_copy_channel_out_func copy_channel_out,
int frame_size,
int decode_fec,
int soft_clip,
void *user_data
);
#endif /* OPUS_PRIVATE_H */

View file

@ -42,18 +42,63 @@
struct OpusProjectionDecoder
{
int demixing_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_out_float(
void *dst,
int dst_stride,
int dst_channel,
const opus_val16 *src,
int src_stride,
int frame_size,
void *user_data)
{
float *float_dst;
const MappingMatrix *matrix;
float_dst = (float *)dst;
matrix = (const MappingMatrix *)user_data;
if (dst_channel == 0)
OPUS_CLEAR(float_dst, frame_size * dst_stride);
if (src != NULL)
mapping_matrix_multiply_channel_out_float(matrix, src, dst_channel,
src_stride, float_dst, dst_stride, frame_size);
}
#endif
static void opus_projection_copy_channel_out_short(
void *dst,
int dst_stride,
int dst_channel,
const opus_val16 *src,
int src_stride,
int frame_size,
void *user_data)
{
opus_int16 *short_dst;
const MappingMatrix *matrix;
short_dst = (opus_int16 *)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_short(matrix, src, dst_channel,
src_stride, short_dst, dst_stride, frame_size);
}
static MappingMatrix *get_demixing_matrix(OpusProjectionDecoder *st)
{
return (MappingMatrix *)((char*)st + align(sizeof(OpusProjectionDecoder)));
return (MappingMatrix*)((char*)st + align(sizeof(OpusProjectionDecoder)));
}
static OpusMSDecoder *get_multistream_decoder(OpusProjectionDecoder *st)
{
return (OpusMSDecoder *)((char*)st + align(sizeof(OpusProjectionDecoder) +
return (OpusMSDecoder*)((char*)st + align(sizeof(OpusProjectionDecoder) +
st->demixing_matrix_size_in_bytes));
}
@ -69,7 +114,7 @@ opus_int32 opus_projection_decoder_get_size(int channels, int streams,
if (!decoder_size)
return 0;
return align(sizeof(OpusProjectionDecoder) + matrix_size + decoder_size);
return align(sizeof(OpusProjectionDecoder)) + matrix_size + decoder_size;
}
int opus_projection_decoder_init(OpusProjectionDecoder *st, opus_int32 Fs,
@ -86,14 +131,14 @@ int opus_projection_decoder_init(OpusProjectionDecoder *st, opus_int32 Fs,
/* 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)
if (align(expected_matrix_size) != align(demixing_matrix_size))
{
RESTORE_STACK;
return OPUS_BAD_ARG;
}
/* Convert demixing matrix input into internal format. */
ALLOC(buf, demixing_matrix_size, opus_int16);
ALLOC(buf, nb_input_streams * channels, opus_int16);
for (i = 0; i < nb_input_streams * channels; i++)
{
int s = demixing_matrix[2*i + 1] << 8 | demixing_matrix[2*i];
@ -102,15 +147,13 @@ int opus_projection_decoder_init(OpusProjectionDecoder *st, opus_int32 Fs,
}
/* Assign demixing matrix. */
st->demixing_matrix_size_in_bytes = expected_matrix_size;
st->demixing_matrix_size_in_bytes = mapping_matrix_get_size(channels, nb_input_streams);
mapping_matrix_init(get_demixing_matrix(st), channels, nb_input_streams, 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);
@ -154,63 +197,33 @@ OpusProjectionDecoder *opus_projection_decoder_create(
return st;
}
#ifdef FIXED_POINT
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;
return opus_multistream_decode_native(get_multistream_decoder(st), data, len,
pcm, opus_projection_copy_channel_out_short, frame_size, decode_fec, 0,
get_demixing_matrix(st));
}
#else
int opus_projection_decode(OpusProjectionDecoder *st, const unsigned char *data,
opus_int32 len, opus_int16 *pcm, int frame_size,
int decode_fec)
{
return opus_multistream_decode_native(get_multistream_decoder(st), data, len,
pcm, opus_projection_copy_channel_out_short, frame_size, decode_fec, 1,
get_demixing_matrix(st));
}
#endif
#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)
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;
return opus_multistream_decode_native(get_multistream_decoder(st), data, len,
pcm, opus_projection_copy_channel_out_float, frame_size, decode_fec, 0,
get_demixing_matrix(st));
}
#endif

View file

@ -42,11 +42,41 @@
struct OpusProjectionEncoder
{
int mixing_matrix_size_in_bytes;
int demixing_matrix_size_in_bytes;
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_;
@ -115,10 +145,8 @@ opus_int32 opus_projection_ambisonics_encoder_get_size(int channels,
ret = get_streams_from_channels(channels, mapping_family, &nb_streams,
&nb_coupled_streams, &order_plus_one);
if (ret != OPUS_OK)
{
if (ret != OPUS_OK || order_plus_one < 2 || order_plus_one > 4)
return 0;
}
matrix_rows = order_plus_one * order_plus_one + 2;
matrix_size = mapping_matrix_get_size(matrix_rows, matrix_rows);
@ -126,7 +154,7 @@ opus_int32 opus_projection_ambisonics_encoder_get_size(int channels,
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);
return align(sizeof(OpusProjectionEncoder)) + matrix_size + matrix_size + encoder_size;
}
int opus_projection_ambisonics_encoder_init(OpusProjectionEncoder *st, opus_int32 Fs,
@ -218,9 +246,7 @@ int opus_projection_ambisonics_encoder_init(OpusProjectionEncoder *st, opus_int3
/* 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);
@ -269,54 +295,31 @@ 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;
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)
{
#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;
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)

View file

@ -47,170 +47,141 @@
#define BUFFER_SIZE 960
#define MAX_DATA_BYTES 32768
#define MAX_FRAME_SAMPLES 5760
#define ERROR_TOLERANCE 1
#define INT16_TO_FLOAT(x) ((1/32768.f)*(float)x)
#define SIMPLE_MATRIX_SIZE 12
#define SIMPLE_MATRIX_FRAME_SIZE 10
#define SIMPLE_MATRIX_INPUT_SIZE 30
#define SIMPLE_MATRIX_OUTPUT_SIZE 40
void print_matrix_short(const opus_int16 *data, int rows, int cols)
int assert_is_equal(
const opus_val16 *a, const opus_int16 *b, int size, opus_int16 tolerance)
{
int i, j;
for (i = 0; i < rows; i++)
int i;
for (i = 0; i < size; i++)
{
for (j = 0; j < cols; j++)
{
fprintf(stderr, "%8.5f ", (float)INT16_TO_FLOAT(data[j * rows + i]));
}
fprintf(stderr, "\n");
#ifdef FIXED_POINT
opus_int16 val = a[i];
#else
opus_int16 val = FLOAT2INT16(a[i]);
#endif
if (abs(val - b[i]) > tolerance)
return 1;
}
fprintf(stderr, "\n");
return 0;
}
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(
int assert_is_equal_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;
return 1;
return 0;
}
int assert_transform_float(
const float *a, const float *b, int size, float tolerance)
void test_simple_matrix(void)
{
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] = {
const MappingMatrix simple_matrix_params = {4, 3, 0};
const opus_int16 simple_matrix_data[SIMPLE_MATRIX_SIZE] = {0, 32767, 0, 0, 32767, 0, 0, 0, 0, 0, 0, 32767};
const opus_int16 input_int16[SIMPLE_MATRIX_INPUT_SIZE] = {
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] = {
const opus_int16 expected_output_int16[SIMPLE_MATRIX_OUTPUT_SIZE] = {
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};
int i, ret;
opus_val16 *input_val16;
opus_val16 *output_val16;
opus_int16 *output_int16;
MappingMatrix *simple_matrix;
/* Convert short to float representations. */
for (i = 0; i < 30; i++)
/* Allocate input/output buffers. */
input_val16 = (opus_val16 *)opus_alloc(align(sizeof(opus_val16) * SIMPLE_MATRIX_INPUT_SIZE));
output_int16 = (opus_int16 *)opus_alloc(align(sizeof(opus_int16) * SIMPLE_MATRIX_OUTPUT_SIZE));
output_val16 = (opus_val16 *)opus_alloc(align(sizeof(opus_val16) * SIMPLE_MATRIX_OUTPUT_SIZE));
/* Initialize matrix */
simple_matrix = (MappingMatrix *)opus_alloc(
mapping_matrix_get_size(simple_matrix_params.rows,
simple_matrix_params.cols));
mapping_matrix_init(simple_matrix, simple_matrix_params.rows,
simple_matrix_params.cols, simple_matrix_params.gain, simple_matrix_data,
sizeof(simple_matrix_data));
/* Copy inputs. */
for (i = 0; i < SIMPLE_MATRIX_INPUT_SIZE; 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;
}
#ifdef FIXED_POINT
input_val16[i] = input_int16[i];
#else
input_val16[i] = (1/32768.f)*input_int16[i];
#endif
opus_free(testing_matrix);
return;
bad_cleanup:
opus_free(testing_matrix);
test_failed();
}
/* _in_short */
for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
output_val16[i] = 0;
for (i = 0; i < simple_matrix->rows; i++)
{
mapping_matrix_multiply_channel_in_short(simple_matrix,
input_int16, simple_matrix->cols, &output_val16[i], i,
simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
}
ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
if (ret)
test_failed();
/* _out_short */
for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
output_int16[i] = 0;
for (i = 0; i < simple_matrix->cols; i++)
{
mapping_matrix_multiply_channel_out_short(simple_matrix,
&input_val16[i], i, simple_matrix->cols, output_int16,
simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
}
ret = assert_is_equal_short(output_int16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
if (ret)
test_failed();
#if !defined(DISABLE_FLOAT_API) && !defined(FIXED_POINT)
/* _in_float */
for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
output_val16[i] = 0;
for (i = 0; i < simple_matrix->rows; i++)
{
mapping_matrix_multiply_channel_in_float(simple_matrix,
input_val16, simple_matrix->cols, &output_val16[i], i,
simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
}
ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
if (ret)
test_failed();
/* _out_float */
for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
output_val16[i] = 0;
for (i = 0; i < simple_matrix->cols; i++)
{
mapping_matrix_multiply_channel_out_float(simple_matrix,
&input_val16[i], i, simple_matrix->cols, output_val16,
simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
}
ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
if (ret)
test_failed();
#endif
opus_free(input_val16);
opus_free(output_int16);
opus_free(output_val16);
opus_free(simple_matrix);
}
void test_creation_arguments(const int channels, const int mapping_family)
@ -403,15 +374,15 @@ int main(int _argc, char **_argv)
(void)_argc;
(void)_argv;
/* Test matrix creation/multiplication. */
test_matrix_transform();
/* Test simple matrix multiplication routines. */
test_simple_matrix();
/* 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);
test_encode_decode(64 * 18, 18, 253);
fprintf(stderr, "All projection tests passed.\n");
return 0;