michael@0: /* Copyright (c) 2011 Xiph.Org Foundation michael@0: Written by Jean-Marc Valin */ michael@0: /* michael@0: Redistribution and use in source and binary forms, with or without michael@0: modification, are permitted provided that the following conditions michael@0: are met: michael@0: michael@0: - Redistributions of source code must retain the above copyright michael@0: notice, this list of conditions and the following disclaimer. michael@0: michael@0: - Redistributions in binary form must reproduce the above copyright michael@0: notice, this list of conditions and the following disclaimer in the michael@0: documentation and/or other materials provided with the distribution. michael@0: michael@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER michael@0: OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, michael@0: EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, michael@0: PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR michael@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF michael@0: LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING michael@0: NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS michael@0: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: michael@0: #ifdef HAVE_CONFIG_H michael@0: #include "config.h" michael@0: #endif michael@0: michael@0: #include "opus_multistream.h" michael@0: #include "opus.h" michael@0: #include "opus_private.h" michael@0: #include "stack_alloc.h" michael@0: #include michael@0: #include "float_cast.h" michael@0: #include "os_support.h" michael@0: michael@0: struct OpusMSDecoder { michael@0: ChannelLayout layout; michael@0: /* Decoder states go here */ michael@0: }; michael@0: michael@0: michael@0: michael@0: michael@0: /* DECODER */ michael@0: michael@0: opus_int32 opus_multistream_decoder_get_size(int nb_streams, int nb_coupled_streams) michael@0: { michael@0: int coupled_size; michael@0: int mono_size; michael@0: michael@0: if(nb_streams<1||nb_coupled_streams>nb_streams||nb_coupled_streams<0)return 0; michael@0: coupled_size = opus_decoder_get_size(2); michael@0: mono_size = opus_decoder_get_size(1); michael@0: return align(sizeof(OpusMSDecoder)) michael@0: + nb_coupled_streams * align(coupled_size) michael@0: + (nb_streams-nb_coupled_streams) * align(mono_size); michael@0: } michael@0: michael@0: int opus_multistream_decoder_init( michael@0: OpusMSDecoder *st, michael@0: opus_int32 Fs, michael@0: int channels, michael@0: int streams, michael@0: int coupled_streams, michael@0: const unsigned char *mapping michael@0: ) michael@0: { michael@0: int coupled_size; michael@0: int mono_size; michael@0: int i, ret; michael@0: char *ptr; michael@0: michael@0: if ((channels>255) || (channels<1) || (coupled_streams>streams) || michael@0: (coupled_streams+streams>255) || (streams<1) || (coupled_streams<0)) michael@0: return OPUS_BAD_ARG; michael@0: michael@0: st->layout.nb_channels = channels; michael@0: st->layout.nb_streams = streams; michael@0: st->layout.nb_coupled_streams = coupled_streams; michael@0: michael@0: for (i=0;ilayout.nb_channels;i++) michael@0: st->layout.mapping[i] = mapping[i]; michael@0: if (!validate_layout(&st->layout)) michael@0: return OPUS_BAD_ARG; michael@0: michael@0: ptr = (char*)st + align(sizeof(OpusMSDecoder)); michael@0: coupled_size = opus_decoder_get_size(2); michael@0: mono_size = opus_decoder_get_size(1); michael@0: michael@0: for (i=0;ilayout.nb_coupled_streams;i++) michael@0: { michael@0: ret=opus_decoder_init((OpusDecoder*)ptr, Fs, 2); michael@0: if(ret!=OPUS_OK)return ret; michael@0: ptr += align(coupled_size); michael@0: } michael@0: for (;ilayout.nb_streams;i++) michael@0: { michael@0: ret=opus_decoder_init((OpusDecoder*)ptr, Fs, 1); michael@0: if(ret!=OPUS_OK)return ret; michael@0: ptr += align(mono_size); michael@0: } michael@0: return OPUS_OK; michael@0: } michael@0: michael@0: michael@0: OpusMSDecoder *opus_multistream_decoder_create( michael@0: opus_int32 Fs, michael@0: int channels, michael@0: int streams, michael@0: int coupled_streams, michael@0: const unsigned char *mapping, michael@0: int *error michael@0: ) michael@0: { michael@0: int ret; michael@0: OpusMSDecoder *st; michael@0: if ((channels>255) || (channels<1) || (coupled_streams>streams) || michael@0: (coupled_streams+streams>255) || (streams<1) || (coupled_streams<0)) michael@0: { michael@0: if (error) michael@0: *error = OPUS_BAD_ARG; michael@0: return NULL; michael@0: } michael@0: st = (OpusMSDecoder *)opus_alloc(opus_multistream_decoder_get_size(streams, coupled_streams)); michael@0: if (st==NULL) michael@0: { michael@0: if (error) michael@0: *error = OPUS_ALLOC_FAIL; michael@0: return NULL; michael@0: } michael@0: ret = opus_multistream_decoder_init(st, Fs, channels, streams, coupled_streams, mapping); michael@0: if (error) michael@0: *error = ret; michael@0: if (ret != OPUS_OK) michael@0: { michael@0: opus_free(st); michael@0: st = NULL; michael@0: } michael@0: return st; michael@0: } michael@0: michael@0: typedef void (*opus_copy_channel_out_func)( michael@0: void *dst, michael@0: int dst_stride, michael@0: int dst_channel, michael@0: const opus_val16 *src, michael@0: int src_stride, michael@0: int frame_size michael@0: ); michael@0: michael@0: static int opus_multistream_packet_validate(const unsigned char *data, michael@0: opus_int32 len, int nb_streams, opus_int32 Fs) michael@0: { michael@0: int s; michael@0: int count; michael@0: unsigned char toc; michael@0: opus_int16 size[48]; michael@0: int samples=0; michael@0: opus_int32 packet_offset; michael@0: michael@0: for (s=0;slayout.nb_streams-1) michael@0: { michael@0: RESTORE_STACK; michael@0: return OPUS_INVALID_PACKET; michael@0: } michael@0: if (!do_plc) michael@0: { michael@0: int ret = opus_multistream_packet_validate(data, len, st->layout.nb_streams, Fs); michael@0: if (ret < 0) michael@0: { michael@0: RESTORE_STACK; michael@0: return ret; michael@0: } else if (ret > frame_size) michael@0: { michael@0: RESTORE_STACK; michael@0: return OPUS_BUFFER_TOO_SMALL; michael@0: } michael@0: } michael@0: for (s=0;slayout.nb_streams;s++) michael@0: { michael@0: OpusDecoder *dec; michael@0: int packet_offset, ret; michael@0: michael@0: dec = (OpusDecoder*)ptr; michael@0: ptr += (s < st->layout.nb_coupled_streams) ? align(coupled_size) : align(mono_size); michael@0: michael@0: if (!do_plc && len<=0) michael@0: { michael@0: RESTORE_STACK; michael@0: return OPUS_INTERNAL_ERROR; michael@0: } michael@0: packet_offset = 0; michael@0: ret = opus_decode_native(dec, data, len, buf, frame_size, decode_fec, s!=st->layout.nb_streams-1, &packet_offset, soft_clip); michael@0: data += packet_offset; michael@0: len -= packet_offset; michael@0: if (ret <= 0) michael@0: { michael@0: RESTORE_STACK; michael@0: return ret; michael@0: } michael@0: frame_size = ret; michael@0: if (s < st->layout.nb_coupled_streams) michael@0: { michael@0: int chan, prev; michael@0: prev = -1; michael@0: /* Copy "left" audio to the channel(s) where it belongs */ michael@0: while ( (chan = get_left_channel(&st->layout, s, prev)) != -1) michael@0: { michael@0: (*copy_channel_out)(pcm, st->layout.nb_channels, chan, michael@0: buf, 2, frame_size); michael@0: prev = chan; michael@0: } michael@0: prev = -1; michael@0: /* Copy "right" audio to the channel(s) where it belongs */ michael@0: while ( (chan = get_right_channel(&st->layout, s, prev)) != -1) michael@0: { michael@0: (*copy_channel_out)(pcm, st->layout.nb_channels, chan, michael@0: buf+1, 2, frame_size); michael@0: prev = chan; michael@0: } michael@0: } else { michael@0: int chan, prev; michael@0: prev = -1; michael@0: /* Copy audio to the channel(s) where it belongs */ michael@0: while ( (chan = get_mono_channel(&st->layout, s, prev)) != -1) michael@0: { michael@0: (*copy_channel_out)(pcm, st->layout.nb_channels, chan, michael@0: buf, 1, frame_size); michael@0: prev = chan; michael@0: } michael@0: } michael@0: } michael@0: /* Handle muted channels */ michael@0: for (c=0;clayout.nb_channels;c++) michael@0: { michael@0: if (st->layout.mapping[c] == 255) michael@0: { michael@0: (*copy_channel_out)(pcm, st->layout.nb_channels, c, michael@0: NULL, 0, frame_size); michael@0: } michael@0: } michael@0: RESTORE_STACK; michael@0: return frame_size; michael@0: } michael@0: michael@0: #if !defined(DISABLE_FLOAT_API) michael@0: static void opus_copy_channel_out_float( michael@0: void *dst, michael@0: int dst_stride, michael@0: int dst_channel, michael@0: const opus_val16 *src, michael@0: int src_stride, michael@0: int frame_size michael@0: ) michael@0: { michael@0: float *float_dst; michael@0: opus_int32 i; michael@0: float_dst = (float*)dst; michael@0: if (src != NULL) michael@0: { michael@0: for (i=0;ilayout.nb_streams;s++) michael@0: { michael@0: OpusDecoder *dec; michael@0: dec = (OpusDecoder*)ptr; michael@0: if (s < st->layout.nb_coupled_streams) michael@0: ptr += align(coupled_size); michael@0: else michael@0: ptr += align(mono_size); michael@0: ret = opus_decoder_ctl(dec, request, &tmp); michael@0: if (ret != OPUS_OK) break; michael@0: *value ^= tmp; michael@0: } michael@0: } michael@0: break; michael@0: case OPUS_RESET_STATE: michael@0: { michael@0: int s; michael@0: for (s=0;slayout.nb_streams;s++) michael@0: { michael@0: OpusDecoder *dec; michael@0: michael@0: dec = (OpusDecoder*)ptr; michael@0: if (s < st->layout.nb_coupled_streams) michael@0: ptr += align(coupled_size); michael@0: else michael@0: ptr += align(mono_size); michael@0: ret = opus_decoder_ctl(dec, OPUS_RESET_STATE); michael@0: if (ret != OPUS_OK) michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: case OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST: michael@0: { michael@0: int s; michael@0: opus_int32 stream_id; michael@0: OpusDecoder **value; michael@0: stream_id = va_arg(ap, opus_int32); michael@0: if (stream_id<0 || stream_id >= st->layout.nb_streams) michael@0: ret = OPUS_BAD_ARG; michael@0: value = va_arg(ap, OpusDecoder**); michael@0: if (!value) michael@0: { michael@0: goto bad_arg; michael@0: } michael@0: for (s=0;slayout.nb_coupled_streams) michael@0: ptr += align(coupled_size); michael@0: else michael@0: ptr += align(mono_size); michael@0: } michael@0: *value = (OpusDecoder*)ptr; michael@0: } michael@0: break; michael@0: case OPUS_SET_GAIN_REQUEST: michael@0: { michael@0: int s; michael@0: /* This works for int32 params */ michael@0: opus_int32 value = va_arg(ap, opus_int32); michael@0: for (s=0;slayout.nb_streams;s++) michael@0: { michael@0: OpusDecoder *dec; michael@0: michael@0: dec = (OpusDecoder*)ptr; michael@0: if (s < st->layout.nb_coupled_streams) michael@0: ptr += align(coupled_size); michael@0: else michael@0: ptr += align(mono_size); michael@0: ret = opus_decoder_ctl(dec, request, value); michael@0: if (ret != OPUS_OK) michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: default: michael@0: ret = OPUS_UNIMPLEMENTED; michael@0: break; michael@0: } michael@0: michael@0: va_end(ap); michael@0: return ret; michael@0: bad_arg: michael@0: va_end(ap); michael@0: return OPUS_BAD_ARG; michael@0: } michael@0: michael@0: michael@0: void opus_multistream_decoder_destroy(OpusMSDecoder *st) michael@0: { michael@0: opus_free(st); michael@0: }