PR #24687 opened by Peter Ross (pross) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24687 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24687.patch
Rhetorex ADPCM decoder and FATE test case. ```fate-samples rhetorex/rhetorex-mono-4bit-8000.wav ``` >From 243cb14d50c3c77887e705dcccad23d58bb4e245 Mon Sep 17 00:00:00 2001 From: Peter Ross <[email protected]> Date: Fri, 20 Jun 2025 21:18:00 +1000 Subject: [PATCH 1/2] avcodec/adpcm: Rhetorex decoder --- libavcodec/Makefile | 1 + libavcodec/adpcm.c | 63 +++++++++++++++++++++++++++++++++++++++++ libavcodec/allcodecs.c | 1 + libavcodec/codec_desc.c | 7 +++++ libavcodec/codec_id.h | 1 + libavformat/riff.c | 1 + libavformat/riffdec.c | 3 ++ 7 files changed, 77 insertions(+) diff --git a/libavcodec/Makefile b/libavcodec/Makefile index e2c60e9051..061a727486 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..e035b50102 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 = (c->coeff1 << 15) + (add >> 1) * c->sample2; + c->coeff1 = av_clip_int16((a - ((a >> 7) & ~0xFF)) >> 15); + + b = (c->coeff2 << 15) + (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 + (rhetorex_index[delta] << 16)) >> 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 dc20b57df4..95704cc38a 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/libavformat/riff.c b/libavformat/riff.c index 21de951e3a..a4cde96806 100644 --- a/libavformat/riff.c +++ b/libavformat/riff.c @@ -564,6 +564,7 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_METASOUND, 0x0075 }, { AV_CODEC_ID_G729, 0x0083 }, { AV_CODEC_ID_AAC, 0x00ff }, + { AV_CODEC_ID_ADPCM_RHETOREX, 0x0100 }, { AV_CODEC_ID_G723_1, 0x0111 }, { AV_CODEC_ID_ADPCM_SANYO, 0x0125 }, { AV_CODEC_ID_SIPR, 0x0130 }, diff --git a/libavformat/riffdec.c b/libavformat/riffdec.c index ffa3fa1100..a132545706 100644 --- a/libavformat/riffdec.c +++ b/libavformat/riffdec.c @@ -255,6 +255,9 @@ int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb, if (par->codec_id == AV_CODEC_ID_ADPCM_G726 && par->sample_rate) par->bits_per_coded_sample = par->bit_rate / par->sample_rate; + if (par->codec_id == AV_CODEC_ID_ADPCM_RHETOREX && par->bits_per_coded_sample == 3) + par->sample_rate = par->sample_rate * 3 / 4; + /* ignore WAVEFORMATEXTENSIBLE layout if different from channel count */ if (channels != par->ch_layout.nb_channels) { av_channel_layout_uninit(&par->ch_layout); -- 2.52.0 >From be03e681ed19e5beeaaf0db2c3164e5ea4d43eae Mon Sep 17 00:00:00 2001 From: Peter Ross <[email protected]> Date: Sat, 28 Jun 2025 10:17:32 +1000 Subject: [PATCH 2/2] tests/fate: Rhetorex ADPCM test case --- tests/fate/audio.mak | 3 +++ tests/ref/fate/rhetorex | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/ref/fate/rhetorex diff --git a/tests/fate/audio.mak b/tests/fate/audio.mak index 60d9b229ac..f1b80da3f2 100644 --- a/tests/fate/audio.mak +++ b/tests/fate/audio.mak @@ -87,6 +87,9 @@ fate-on2avc: CMD = framecrc -i $(TARGET_SAMPLES)/vp7/potter-40.vp7 -frames 30 -v FATE_SAMPLES_AUDIO-$(call FRAMECRC, PAF, PAF_AUDIO) += fate-paf-audio fate-paf-audio: CMD = framecrc -i $(TARGET_SAMPLES)/paf/hod1-partial.paf -vn +FATE_SAMPLES_AUDIO-$(call FRAMECRC, WAV, ADPCM_RHETOREX) += fate-rhetorex +fate-rhetorex: CMD = framecrc -i $(TARGET_SAMPLES)/rhetorex/rhetorex-mono-4bit-8000.wav + FATE_SAMPLES_AUDIO-$(call FRAMECRC, VMD, VMDAUDIO, ARESAMPLE_FILTER) += fate-sierra-vmd-audio fate-sierra-vmd-audio: CMD = framecrc -i $(TARGET_SAMPLES)/vmd/12.vmd -vn -af aresample diff --git a/tests/ref/fate/rhetorex b/tests/ref/fate/rhetorex new file mode 100644 index 0000000000..6d1adac607 --- /dev/null +++ b/tests/ref/fate/rhetorex @@ -0,0 +1,45 @@ +#tb 0: 1/8000 +#media_type 0: audio +#codec_id 0: pcm_s16le +#sample_rate 0: 8000 +#channel_layout_name 0: mono +0, 0, 0, 512, 1024, 0x8eb9f551 +0, 512, 512, 512, 1024, 0xdfe243c6 +0, 1024, 1024, 512, 1024, 0x2507ee8d +0, 1536, 1536, 512, 1024, 0x35bdf8de +0, 2048, 2048, 512, 1024, 0xdbb714be +0, 2560, 2560, 512, 1024, 0x7ae51702 +0, 3072, 3072, 512, 1024, 0x5da616f0 +0, 3584, 3584, 512, 1024, 0x5502ff8b +0, 4096, 4096, 512, 1024, 0x5c2d0550 +0, 4608, 4608, 512, 1024, 0x2054f93d +0, 5120, 5120, 512, 1024, 0xca7efde9 +0, 5632, 5632, 512, 1024, 0x0801ec51 +0, 6144, 6144, 512, 1024, 0xc04600be +0, 6656, 6656, 512, 1024, 0x89711604 +0, 7168, 7168, 512, 1024, 0x96fb0e93 +0, 7680, 7680, 512, 1024, 0xc085e949 +0, 8192, 8192, 512, 1024, 0xe2e6fb97 +0, 8704, 8704, 512, 1024, 0x6093f2cc +0, 9216, 9216, 512, 1024, 0xf9b80ca4 +0, 9728, 9728, 512, 1024, 0x9a46f10b +0, 10240, 10240, 512, 1024, 0xfdade14e +0, 10752, 10752, 512, 1024, 0x49f3f1c7 +0, 11264, 11264, 512, 1024, 0x141ff60f +0, 11776, 11776, 512, 1024, 0xa3720f99 +0, 12288, 12288, 512, 1024, 0x75a601cb +0, 12800, 12800, 512, 1024, 0xa9931362 +0, 13312, 13312, 512, 1024, 0x241cfd4d +0, 13824, 13824, 512, 1024, 0x12142acd +0, 14336, 14336, 512, 1024, 0x905727f9 +0, 14848, 14848, 512, 1024, 0x7f940c1f +0, 15360, 15360, 512, 1024, 0x25540182 +0, 15872, 15872, 512, 1024, 0x3990e961 +0, 16384, 16384, 512, 1024, 0x1b97066b +0, 16896, 16896, 512, 1024, 0xac79ff32 +0, 17408, 17408, 512, 1024, 0x5b20eb00 +0, 17920, 17920, 512, 1024, 0x6566022a +0, 18432, 18432, 512, 1024, 0x934ce6ed +0, 18944, 18944, 512, 1024, 0x10d3fd0f +0, 19456, 19456, 512, 1024, 0x10d3fd0f +0, 19968, 19968, 512, 1024, 0x10d3fd0f -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
