1/* 2 * RoQ audio encoder 3 * 4 * Copyright (c) 2005 Eric Lasota 5 * Based on RoQ specs (c)2001 Tim Ferguson 6 * 7 * This file is part of FFmpeg. 8 * 9 * FFmpeg is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * FFmpeg is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with FFmpeg; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24#include "libavutil/intmath.h" 25#include "avcodec.h" 26#include "bytestream.h" 27 28#define ROQ_FIRST_FRAME_SIZE (735*8) 29#define ROQ_FRAME_SIZE 735 30 31 32#define MAX_DPCM (127*127) 33 34 35typedef struct 36{ 37 short lastSample[2]; 38} ROQDPCMContext; 39 40static av_cold int roq_dpcm_encode_init(AVCodecContext *avctx) 41{ 42 ROQDPCMContext *context = avctx->priv_data; 43 44 if (avctx->channels > 2) { 45 av_log(avctx, AV_LOG_ERROR, "Audio must be mono or stereo\n"); 46 return -1; 47 } 48 if (avctx->sample_rate != 22050) { 49 av_log(avctx, AV_LOG_ERROR, "Audio must be 22050 Hz\n"); 50 return -1; 51 } 52 if (avctx->sample_fmt != SAMPLE_FMT_S16) { 53 av_log(avctx, AV_LOG_ERROR, "Audio must be signed 16-bit\n"); 54 return -1; 55 } 56 57 avctx->frame_size = ROQ_FIRST_FRAME_SIZE; 58 59 context->lastSample[0] = context->lastSample[1] = 0; 60 61 avctx->coded_frame= avcodec_alloc_frame(); 62 avctx->coded_frame->key_frame= 1; 63 64 return 0; 65} 66 67static unsigned char dpcm_predict(short *previous, short current) 68{ 69 int diff; 70 int negative; 71 int result; 72 int predicted; 73 74 diff = current - *previous; 75 76 negative = diff<0; 77 diff = FFABS(diff); 78 79 if (diff >= MAX_DPCM) 80 result = 127; 81 else { 82 result = ff_sqrt(diff); 83 result += diff > result*result+result; 84 } 85 86 /* See if this overflows */ 87 retry: 88 diff = result*result; 89 if (negative) 90 diff = -diff; 91 predicted = *previous + diff; 92 93 /* If it overflows, back off a step */ 94 if (predicted > 32767 || predicted < -32768) { 95 result--; 96 goto retry; 97 } 98 99 /* Add the sign bit */ 100 result |= negative << 7; //if (negative) result |= 128; 101 102 *previous = predicted; 103 104 return result; 105} 106 107static int roq_dpcm_encode_frame(AVCodecContext *avctx, 108 unsigned char *frame, int buf_size, void *data) 109{ 110 int i, samples, stereo, ch; 111 short *in; 112 unsigned char *out; 113 114 ROQDPCMContext *context = avctx->priv_data; 115 116 stereo = (avctx->channels == 2); 117 118 if (stereo) { 119 context->lastSample[0] &= 0xFF00; 120 context->lastSample[1] &= 0xFF00; 121 } 122 123 out = frame; 124 in = data; 125 126 bytestream_put_byte(&out, stereo ? 0x21 : 0x20); 127 bytestream_put_byte(&out, 0x10); 128 bytestream_put_le32(&out, avctx->frame_size*avctx->channels); 129 130 if (stereo) { 131 bytestream_put_byte(&out, (context->lastSample[1])>>8); 132 bytestream_put_byte(&out, (context->lastSample[0])>>8); 133 } else 134 bytestream_put_le16(&out, context->lastSample[0]); 135 136 /* Write the actual samples */ 137 samples = avctx->frame_size; 138 for (i=0; i<samples; i++) 139 for (ch=0; ch<avctx->channels; ch++) 140 *out++ = dpcm_predict(&context->lastSample[ch], *in++); 141 142 /* Use smaller frames from now on */ 143 avctx->frame_size = ROQ_FRAME_SIZE; 144 145 /* Return the result size */ 146 return out - frame; 147} 148 149static av_cold int roq_dpcm_encode_close(AVCodecContext *avctx) 150{ 151 av_freep(&avctx->coded_frame); 152 153 return 0; 154} 155 156AVCodec roq_dpcm_encoder = { 157 "roq_dpcm", 158 AVMEDIA_TYPE_AUDIO, 159 CODEC_ID_ROQ_DPCM, 160 sizeof(ROQDPCMContext), 161 roq_dpcm_encode_init, 162 roq_dpcm_encode_frame, 163 roq_dpcm_encode_close, 164 NULL, 165 .sample_fmts = (const enum SampleFormat[]){SAMPLE_FMT_S16,SAMPLE_FMT_NONE}, 166 .long_name = NULL_IF_CONFIG_SMALL("id RoQ DPCM"), 167}; 168