Commit 060b019b5f for ffmpeg

commit 060b019b5f73d50b06038c813e32d5297df658bf
Author: Peter Ross <pross@xvid.org>
Date:   Fri Jun 20 21:18:00 2025 +1000

    avcodec/adpcm: Rhetorex ADPCM decoder

diff --git a/Changelog b/Changelog
index 1cfa9db1b6..56ecd0b5d6 100644
--- a/Changelog
+++ b/Changelog
@@ -14,6 +14,7 @@ version <next>:
 - NVIDIA optical flow accelerated interpolation filter (vf_fruc_vulkan)
 - H.264 data partitioning support
 - DSD (dsd_msbf) encoder
+- Rhetorex ADPCM decoder


 version 9.0:
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index b041518093..509317c83a 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -1346,6 +1346,7 @@ following image formats are supported:
 @item ADPCM Nintendo THP  @tab     @tab  X
 @item ADPCM Playstation      @tab     @tab  X
 @item ADPCM QT IMA           @tab  X  @tab  X
+@item ADPCM Rhetorex         @tab     @tab  X
 @item ADPCM Sanyo            @tab     @tab  X
 @item ADPCM SEGA CRI ADX     @tab  X  @tab  X
     @tab Used in Sega Dreamcast games.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 21522f315f..7785638c77 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1022,6 +1022,7 @@ OBJS-$(CONFIG_ADPCM_MTAF_DECODER)         += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_N64_DECODER)          += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_PSX_DECODER)          += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_PSXC_DECODER)         += adpcm.o adpcm_data.o
+OBJS-$(CONFIG_ADPCM_RHETOREX_DECODER)     += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_SANYO_DECODER)        += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_SBPRO_2_DECODER)      += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_SBPRO_3_DECODER)      += adpcm.o adpcm_data.o
diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c
index 74af2cb4d3..a53a4c7c57 100644
--- a/libavcodec/adpcm.c
+++ b/libavcodec/adpcm.c
@@ -241,6 +241,29 @@ static const int8_t mtf_index_table[16] = {
     -1, -1, -1, -1,  2,  4,  6,  8,
 };

+static const int16_t rhetorex_step[128] = {
+       40,   130,   232,   350,   493,   673,   927,  1382,
+       60,   195,   348,   526,   740,  1010,  1391,  2074,
+       85,   277,   494,   745,  1048,  1431,  1971,  2938,
+      121,   391,   697,  1052,  1480,  2021,  2782,  4148,
+      176,   570,  1017,  1535,  2158,  2947,  4058,  6050,
+      257,   831,  1482,  2236,  3145,  4295,  5913,  8815,
+      373,  1206,  2151,  3245,  4564,  6232,  8579, 12791,
+      534,  1728,  3082,  4649,  6538,  8928, 12290, 18323,
+      782,  2527,  4507,  6798,  9560, 13055, 17971, 26793,
+     1115,  3603,  6426,  9692, 13631, 18614, 25623, 32767,
+     1620,  5233,  9334, 14078, 19799, 27036, 32767, 32767,
+     2361,  7630, 13608, 20526, 28866, 32767, 32767, 32767,
+     3447, 11136, 19860, 29955, 32767, 32767, 32767, 32767,
+     4865, 15717, 28031, 32767, 32767, 32767, 32767, 32767,
+     6888, 22255, 32767, 32767, 32767, 32767, 32767, 32767,
+   -10336, 32144,  5984,-24288,  4752, 24112, 24688,-26336,
+};
+
+static const int16_t rhetorex_index[8] = {
+      -83,   -55,   -27,   163,   374,   677,  1864,  3408,
+};
+
 /* end of tables */

 typedef struct ADPCMDecodeContext {
@@ -262,6 +285,7 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx)
     switch(avctx->codec->id) {
     case AV_CODEC_ID_ADPCM_IMA_AMV:
     case AV_CODEC_ID_ADPCM_N64:
+    case AV_CODEC_ID_ADPCM_RHETOREX:
         max_channels = 1;
         break;
     case AV_CODEC_ID_ADPCM_SANYO:
@@ -721,6 +745,34 @@ static inline int16_t adpcm_ct_expand_nibble(ADPCMChannelStatus *c, int8_t nibbl
     return (int16_t)c->predictor;
 }

+static inline int16_t adpcm_rhetorex_expand_nibble(ADPCMChannelStatus *c, uint16_t nibble)
+{
+    int delta, add, a, b;
+    int16_t sample;
+
+    delta = nibble & 7;
+    add = rhetorex_step[((c->step_index >> 8) & 0xF8) + delta];
+    if (nibble & 0x8)
+        add = -add;
+
+    sample = av_clip_int16(c->predictor + add);
+
+    a = 32768 * c->coeff1 + (add >> 1) * c->sample2;
+    c->coeff1 = av_clip_int16((a - ((a >> 7) & ~0xFF)) >> 15);
+
+    b = 32768 * c->coeff2 + (add >> 1) * c->sample1;
+    c->coeff2 = av_clip_int16((b - ((b >> 7) & ~0xFF) + 0x800000) >> 15);
+
+    c->predictor = av_clip_int16((c->coeff1 * c->sample1 + c->coeff2 * sample) >> 15);
+
+    c->sample2 = c->sample1;
+    c->sample1 = sample;
+
+    c->step_index = FFABS(64512 * c->step_index + 65536 * rhetorex_index[delta]) >> 16;
+
+    return sample;
+}
+
 static inline int16_t adpcm_sbpro_expand_nibble(ADPCMChannelStatus *c, int8_t nibble, int size, int shift)
 {
     int sign, delta, diff;
@@ -1418,6 +1470,9 @@ static int get_nb_samples(AVCodecContext *avctx, GetByteContext *gb,
     case AV_CODEC_ID_ADPCM_PSXC:
         nb_samples = ((buf_size - 1) / ch) * 2;
         break;
+    case AV_CODEC_ID_ADPCM_RHETOREX:
+        nb_samples = buf_size * 2;
+        break;
     case AV_CODEC_ID_ADPCM_ARGO:
         nb_samples = buf_size / avctx->block_align * 32;
         break;
@@ -2825,6 +2880,13 @@ static int adpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame,
         align_get_bits(&g);
         bytestream2_skip(&gb, get_bits_count(&g) / 8);
         ) /* End of CASE */
+    CASE(ADPCM_RHETOREX,
+        for (int i = 0; i < nb_samples / 2; i++) {
+            uint8_t byte = bytestream2_get_byteu(&gb);
+            *samples++ = adpcm_rhetorex_expand_nibble(c->status, byte >> 4);
+            *samples++ = adpcm_rhetorex_expand_nibble(c->status, byte);
+        }
+        ) /* End of CASE */
     CASE(ADPCM_ARGO,
         /*
          * The format of each block:
@@ -3015,6 +3077,7 @@ ADPCM_DECODER(ADPCM_MTAF,        adpcm_mtaf,        "ADPCM MTAF")
 ADPCM_DECODER(ADPCM_N64,         adpcm_n64,         "ADPCM Silicon Graphics N64")
 ADPCM_DECODER(ADPCM_PSX,         adpcm_psx,         "ADPCM Playstation")
 ADPCM_DECODER(ADPCM_PSXC,        adpcm_psxc,        "ADPCM Playstation C")
+ADPCM_DECODER(ADPCM_RHETOREX,    adpcm_rhetorex,    "ADPCM Rhetorex")
 ADPCM_DECODER(ADPCM_SANYO,       adpcm_sanyo,       "ADPCM Sanyo")
 ADPCM_DECODER(ADPCM_SBPRO_2,     adpcm_sbpro_2,     "ADPCM Sound Blaster Pro 2-bit")
 ADPCM_DECODER(ADPCM_SBPRO_3,     adpcm_sbpro_3,     "ADPCM Sound Blaster Pro 2.6-bit")
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index ca20c76fe6..81536329d0 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -699,6 +699,7 @@ extern const FFCodec ff_adpcm_mtaf_decoder;
 extern const FFCodec ff_adpcm_n64_decoder;
 extern const FFCodec ff_adpcm_psx_decoder;
 extern const FFCodec ff_adpcm_psxc_decoder;
+extern const FFCodec ff_adpcm_rhetorex_decoder;
 extern const FFCodec ff_adpcm_sanyo_decoder;
 extern const FFCodec ff_adpcm_sbpro_2_decoder;
 extern const FFCodec ff_adpcm_sbpro_3_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index c8bde3d5b2..37dafd47f4 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -2698,6 +2698,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Acorn Escape"),
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_ADPCM_RHETOREX,
+        .type      = AVMEDIA_TYPE_AUDIO,
+        .name      = "adpcm_rhetorex",
+        .long_name = NULL_IF_CONFIG_SMALL("ADPCM Rhetorex"),
+        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
+    },

     /* AMR */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 841aa28d20..5fe44bbf90 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -430,6 +430,7 @@ enum AVCodecID {
     AV_CODEC_ID_ADPCM_PSXC,
     AV_CODEC_ID_ADPCM_CIRCUS,
     AV_CODEC_ID_ADPCM_IMA_ESCAPE,
+    AV_CODEC_ID_ADPCM_RHETOREX,

     /* AMR */
     AV_CODEC_ID_AMR_NB = 0x12000,
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index cb6cf2d6a1..f87fc13873 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -479,6 +479,7 @@ int av_get_exact_bits_per_sample(enum AVCodecID codec_id)
     case AV_CODEC_ID_ADPCM_G722:
     case AV_CODEC_ID_ADPCM_YAMAHA:
     case AV_CODEC_ID_ADPCM_AICA:
+    case AV_CODEC_ID_ADPCM_RHETOREX:
         return 4;
     case AV_CODEC_ID_DSD_LSBF:
     case AV_CODEC_ID_DSD_MSBF: