libavcodec/ws-snd1.c
034eeaa1
 /*
  * Westwood SNDx codecs
  * Copyright (c) 2005 Konstantin Shishkov
  *
b78e7197
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
034eeaa1
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
b78e7197
  * version 2.1 of the License, or (at your option) any later version.
034eeaa1
  *
b78e7197
  * FFmpeg is distributed in the hope that it will be useful,
034eeaa1
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
b78e7197
  * License along with FFmpeg; if not, write to the Free Software
5509bffa
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
034eeaa1
  */
6a5d31ac
 
ef4a9342
 #include <stdint.h>
5459848b
 
a903f8f0
 #include "libavutil/channel_layout.h"
1d9c2dc8
 #include "libavutil/common.h"
6a5d31ac
 #include "libavutil/intreadwrite.h"
034eeaa1
 #include "avcodec.h"
594d4d5d
 #include "internal.h"
034eeaa1
 
 /**
ba87f080
  * @file
3d371f41
  * Westwood SNDx codecs
034eeaa1
  *
  * Reference documents about VQA format and its audio codecs
  * can be found here:
  * http://www.multimedia.cx
  */
 
774b20ca
 static const int8_t ws_adpcm_4bit[] = {
034eeaa1
     -9, -8, -6, -5, -4, -3, -2, -1,
3d371f41
      0,  1,  2,  3,  4,  5,  6,  8
 };
034eeaa1
 
3d371f41
 static av_cold int ws_snd_decode_init(AVCodecContext *avctx)
034eeaa1
 {
5459848b
     avctx->channels       = 1;
     avctx->channel_layout = AV_CH_LAYOUT_MONO;
     avctx->sample_fmt     = AV_SAMPLE_FMT_U8;
0eea2129
 
034eeaa1
     return 0;
 }
 
3d371f41
 static int ws_snd_decode_frame(AVCodecContext *avctx, void *data,
0eea2129
                                int *got_frame_ptr, AVPacket *avpkt)
034eeaa1
 {
8ae50d87
     AVFrame *frame     = data;
7a00bbad
     const uint8_t *buf = avpkt->data;
3d371f41
     int buf_size       = avpkt->size;
115329f1
 
0eea2129
     int in_size, out_size, ret;
2322ced8
     int sample = 128;
0eea2129
     uint8_t *samples;
6896aa7a
     uint8_t *samples_end;
115329f1
 
034eeaa1
     if (!buf_size)
         return 0;
 
417364ce
     if (buf_size < 4) {
         av_log(avctx, AV_LOG_ERROR, "packet is too small\n");
         return AVERROR(EINVAL);
     }
 
fead30d4
     out_size = AV_RL16(&buf[0]);
3d371f41
     in_size  = AV_RL16(&buf[2]);
034eeaa1
     buf += 4;
115329f1
 
e938637b
     if (in_size > buf_size) {
         av_log(avctx, AV_LOG_ERROR, "Frame data is larger than input buffer\n");
e4af8ed8
         return AVERROR_INVALIDDATA;
e938637b
     }
0eea2129
 
     /* get output buffer */
8ae50d87
     frame->nb_samples = out_size;
     if ((ret = ff_get_buffer(avctx, frame)) < 0) {
0eea2129
         av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
         return ret;
     }
8ae50d87
     samples     = frame->data[0];
6896aa7a
     samples_end = samples + out_size;
2322ced8
 
034eeaa1
     if (in_size == out_size) {
618b067d
         memcpy(samples, buf, out_size);
8ae50d87
         *got_frame_ptr = 1;
034eeaa1
         return buf_size;
     }
115329f1
 
6896aa7a
     while (samples < samples_end && buf - avpkt->data < buf_size) {
417364ce
         int code, smp, size;
034eeaa1
         uint8_t count;
3d371f41
         code  = *buf >> 6;
         count = *buf & 0x3F;
034eeaa1
         buf++;
417364ce
 
6896aa7a
         /* make sure we don't write past the output buffer */
417364ce
         switch (code) {
9fb7a5af
         case 0:  smp = 4 * (count + 1);                break;
         case 1:  smp = 2 * (count + 1);                break;
417364ce
         case 2:  smp = (count & 0x20) ? 1 : count + 1; break;
         default: smp = count + 1;                      break;
         }
6896aa7a
         if (samples_end - samples < smp)
417364ce
             break;
 
         /* make sure we don't read past the input buffer */
         size = ((code == 2 && (count & 0x20)) || code == 3) ? 0 : count + 1;
         if ((buf - avpkt->data) + size > buf_size)
             break;
 
3d371f41
         switch (code) {
034eeaa1
         case 0: /* ADPCM 2-bit */
             for (count++; count > 0; count--) {
                 code = *buf++;
659c719b
                 sample += ( code       & 0x3) - 2;
2322ced8
                 sample = av_clip_uint8(sample);
                 *samples++ = sample;
659c719b
                 sample += ((code >> 2) & 0x3) - 2;
2322ced8
                 sample = av_clip_uint8(sample);
                 *samples++ = sample;
659c719b
                 sample += ((code >> 4) & 0x3) - 2;
2322ced8
                 sample = av_clip_uint8(sample);
                 *samples++ = sample;
659c719b
                 sample +=  (code >> 6)        - 2;
2322ced8
                 sample = av_clip_uint8(sample);
                 *samples++ = sample;
034eeaa1
             }
             break;
         case 1: /* ADPCM 4-bit */
             for (count++; count > 0; count--) {
                 code = *buf++;
                 sample += ws_adpcm_4bit[code & 0xF];
2322ced8
                 sample = av_clip_uint8(sample);
                 *samples++ = sample;
034eeaa1
                 sample += ws_adpcm_4bit[code >> 4];
2322ced8
                 sample = av_clip_uint8(sample);
                 *samples++ = sample;
034eeaa1
             }
             break;
         case 2: /* no compression */
             if (count & 0x20) { /* big delta */
774b20ca
                 int8_t t;
034eeaa1
                 t = count;
                 t <<= 3;
                 sample += t >> 3;
2322ced8
                 sample = av_clip_uint8(sample);
                 *samples++ = sample;
034eeaa1
             } else { /* copy */
618b067d
                 memcpy(samples, buf, smp);
                 samples += smp;
                 buf     += smp;
2322ced8
                 sample = buf[-1];
034eeaa1
             }
             break;
         default: /* run */
618b067d
             memset(samples, sample, smp);
             samples += smp;
034eeaa1
         }
     }
115329f1
 
8ae50d87
     frame->nb_samples = samples - frame->data[0];
     *got_frame_ptr    = 1;
417364ce
 
034eeaa1
     return buf_size;
 }
 
e7e2df27
 AVCodec ff_ws_snd1_decoder = {
ec6402b7
     .name           = "ws_snd1",
     .type           = AVMEDIA_TYPE_AUDIO,
36ef5369
     .id             = AV_CODEC_ID_WESTWOOD_SND1,
ec6402b7
     .init           = ws_snd_decode_init,
     .decode         = ws_snd_decode_frame,
0eea2129
     .capabilities   = CODEC_CAP_DR1,
00c3b67b
     .long_name      = NULL_IF_CONFIG_SMALL("Westwood Audio (SND1)"),
034eeaa1
 };