Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Peter Ross <pross@xvid.org>
To: ffmpeg-devel@ffmpeg.org
Subject: [FFmpeg-devel] [PATCH 1/2] avcodec/adpcm: Sanyo LD-ADPCM decoder
Date: Wed, 25 Jun 2025 18:02:56 +1000
Message-ID: <7bb76ae91079c70434e8069230d5e7a4d11a152e.1750838520.git.pross@xvid.org> (raw)


[-- Attachment #1.1: Type: text/plain, Size: 12884 bytes --]

---
adpcm_le.c was added because the decoder needs little-endian get_bits.h

Samples:
https://pross.sdf.org/sandpit/sanyo-mono-3bit-8000.wav
https://pross.sdf.org/sandpit/sanyo-mono-4bit-8000.wav
https://pross.sdf.org/sandpit/sanyo-mono-5bit-8000.wav

 libavcodec/Makefile     |   1 +
 libavcodec/adpcm.c      |  21 ++++
 libavcodec/adpcm.h      |   2 +
 libavcodec/adpcm_le.c   | 205 ++++++++++++++++++++++++++++++++++++++++
 libavcodec/allcodecs.c  |   1 +
 libavcodec/codec_desc.c |   7 ++
 libavcodec/codec_id.h   |   1 +
 libavformat/riff.c      |   1 +
 8 files changed, 239 insertions(+)
 create mode 100644 libavcodec/adpcm_le.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index dca6c100c0..45cf80e5dd 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -990,6 +990,7 @@ OBJS-$(CONFIG_ADPCM_MS_DECODER)           += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_MS_ENCODER)           += adpcmenc.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_MTAF_DECODER)         += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_PSX_DECODER)          += adpcm.o adpcm_data.o
+OBJS-$(CONFIG_ADPCM_SANYO_DECODER)        += adpcm.o adpcm_data.o adpcm_le.o
 OBJS-$(CONFIG_ADPCM_SBPRO_2_DECODER)      += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_SBPRO_3_DECODER)      += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_SBPRO_4_DECODER)      += adpcm.o adpcm_data.o
diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c
index 622cf54b40..d0f5780dce 100644
--- a/libavcodec/adpcm.c
+++ b/libavcodec/adpcm.c
@@ -260,6 +260,9 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx)
     case AV_CODEC_ID_ADPCM_IMA_AMV:
         max_channels = 1;
         break;
+    case AV_CODEC_ID_ADPCM_SANYO:
+        max_channels = 2;
+        break;
     case AV_CODEC_ID_ADPCM_AFC:
     case AV_CODEC_ID_ADPCM_EA_R1:
     case AV_CODEC_ID_ADPCM_EA_R2:
@@ -307,6 +310,10 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx)
             avctx->block_align != 17 * avctx->ch_layout.nb_channels)
             return AVERROR_INVALIDDATA;
         break;
+    case AV_CODEC_ID_ADPCM_SANYO:
+        if (avctx->bits_per_coded_sample < 3 || avctx->bits_per_coded_sample > 5)
+            return AVERROR_INVALIDDATA;
+        break;
     case AV_CODEC_ID_ADPCM_IMA_XBOX:
         if (avctx->bits_per_coded_sample != 4)
             return AVERROR_INVALIDDATA;
@@ -338,6 +345,7 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx)
     case AV_CODEC_ID_ADPCM_AFC:
     case AV_CODEC_ID_ADPCM_DTK:
     case AV_CODEC_ID_ADPCM_PSX:
+    case AV_CODEC_ID_ADPCM_SANYO:
     case AV_CODEC_ID_ADPCM_MTAF:
     case AV_CODEC_ID_ADPCM_ARGO:
     case AV_CODEC_ID_ADPCM_IMA_MOFLEX:
@@ -1072,6 +1080,11 @@ static int get_nb_samples(AVCodecContext *avctx, GetByteContext *gb,
     case AV_CODEC_ID_ADPCM_ZORK:
         nb_samples = buf_size / ch;
         break;
+    case AV_CODEC_ID_ADPCM_SANYO:
+        if (!avctx->extradata || avctx->extradata_size != 2)
+            return AVERROR_INVALIDDATA;
+        nb_samples = AV_RL16(avctx->extradata);
+        break;
     }
 
     /* validate coded sample count */
@@ -2265,6 +2278,13 @@ static int adpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame,
             }
         }
         ) /* End of CASE */
+    CASE(ADPCM_SANYO,
+        for (int ch = 0; ch < channels; ch++) {
+            c->status[ch].predictor = (int16_t)bytestream2_get_le16(&gb);
+            c->status[ch].step = (int16_t)bytestream2_get_le16(&gb);
+        }
+        bytestream2_skip(&gb, ff_adpcm_sanyo_decode(c->status, gb.buffer, bytestream2_get_bytes_left(&gb), avctx->bits_per_coded_sample, nb_samples, channels, samples_p));
+        ) /* End of CASE */
     CASE(ADPCM_ARGO,
         /*
          * The format of each block:
@@ -2448,6 +2468,7 @@ ADPCM_DECODER(ADPCM_IMA_XBOX,    sample_fmts_s16p, adpcm_ima_xbox,    "ADPCM IMA
 ADPCM_DECODER(ADPCM_MS,          sample_fmts_both, adpcm_ms,          "ADPCM Microsoft")
 ADPCM_DECODER(ADPCM_MTAF,        sample_fmts_s16p, adpcm_mtaf,        "ADPCM MTAF")
 ADPCM_DECODER(ADPCM_PSX,         sample_fmts_s16p, adpcm_psx,         "ADPCM Playstation")
+ADPCM_DECODER(ADPCM_SANYO,       sample_fmts_s16p, adpcm_sanyo,       "ADPCM Sanyo")
 ADPCM_DECODER(ADPCM_SBPRO_2,     sample_fmts_s16,  adpcm_sbpro_2,     "ADPCM Sound Blaster Pro 2-bit")
 ADPCM_DECODER(ADPCM_SBPRO_3,     sample_fmts_s16,  adpcm_sbpro_3,     "ADPCM Sound Blaster Pro 2.6-bit")
 ADPCM_DECODER(ADPCM_SBPRO_4,     sample_fmts_s16,  adpcm_sbpro_4,     "ADPCM Sound Blaster Pro 4-bit")
diff --git a/libavcodec/adpcm.h b/libavcodec/adpcm.h
index 0ffc3da1d0..b1c54ed275 100644
--- a/libavcodec/adpcm.h
+++ b/libavcodec/adpcm.h
@@ -45,4 +45,6 @@ typedef struct ADPCMChannelStatus {
 
 int16_t ff_adpcm_argo_expand_nibble(ADPCMChannelStatus *cs, int nibble, int shift, int flag);
 
+int ff_adpcm_sanyo_decode(ADPCMChannelStatus *cs, const uint8_t *data, int data_size, int bits_per_coded_sample, int nb_samples, int channels, int16_t **samples_p);
+
 #endif /* AVCODEC_ADPCM_H */
diff --git a/libavcodec/adpcm_le.c b/libavcodec/adpcm_le.c
new file mode 100644
index 0000000000..df8eb3dd90
--- /dev/null
+++ b/libavcodec/adpcm_le.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2025 Peter Ross
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * 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
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "adpcm.h"
+#define BITSTREAM_READER_LE
+#include "get_bits.h"
+
+static int adpcm_sanyo_expand3(ADPCMChannelStatus *c, int bits)
+{
+    int sign, delta, add;
+
+    sign = bits & 4;
+    if (sign)
+        delta = 4 - (bits & 3);
+    else
+        delta = bits;
+
+    switch (delta) {
+    case 0:
+        add = 0;
+        c->step = (3 * c->step) >> 2;
+        break;
+    case 1:
+        add = c->step;
+        c->step = (4 * c->step - (c->step >> 1)) >> 2;
+        break;
+    case 2:
+        add = 2 * c->step;
+        c->step = ((c->step >> 1) + add) >> 1;
+        break;
+    case 3:
+        add = 4 * c->step - (c->step >> 1);
+        c->step = 2 * c->step;
+        break;
+    case 4:
+        add = (11 * c->step) >> 1;
+        c->step = 3 * c->step;
+        break;
+    }
+
+    if (sign)
+        add = -add;
+
+    c->predictor = av_clip_int16(c->predictor + add);
+    c->step = av_clip(c->step, 1, 7281);
+    return c->predictor;
+}
+
+static int adpcm_sanyo_expand4(ADPCMChannelStatus *c, int bits)
+{
+    int sign, delta, add;
+
+    sign = bits & 8;
+    if (sign)
+        delta = 8 - (bits & 7);
+    else
+        delta = bits;
+
+    switch (delta) {
+    case 0:
+        add = 0;
+        c->step = (3 * c->step) >> 2;
+        break;
+    case 1:
+        add = c->step;
+        c->step = (3 * c->step) >> 2;
+        break;
+    case 2:
+        add = 2 * c->step;
+        break;
+    case 3:
+        add = 3 * c->step;
+        break;
+    case 4:
+        add = 4 * c->step;
+        break;
+    case 5:
+        add = (11 * c->step) >> 1;
+        c->step += c->step >> 2;
+        break;
+    case 6:
+        add = (15 * c->step) >> 1;
+        c->step = 2 * c->step;
+        break;
+    case 7:
+        if (sign)
+            add = (19 * c->step) >> 1;
+        else
+            add = (21 * c->step) >> 1;
+        c->step = (c->step >> 1) + 2 * c->step;
+        break;
+    case 8:
+        add = (25 * c->step) >> 1;
+        c->step = 5 * c->step;
+        break;
+    }
+
+    if (sign)
+        add = -add;
+
+    c->predictor = av_clip_int16(c->predictor + add);
+    c->step = av_clip(c->step, 1, 2621);
+    return c->predictor;
+}
+
+static int adpcm_sanyo_expand5(ADPCMChannelStatus *c, int bits)
+{
+    int sign, delta, add;
+
+    sign = bits & 0x10;
+    if (sign)
+        delta = 16 - (bits & 0xF);
+    else
+        delta = bits;
+
+    add = delta * c->step;
+    switch (delta) {
+    case 0:
+        c->step += (c->step >> 2) - (c->step >> 1);
+        break;
+    case 1:
+    case 2:
+    case 3:
+        c->step += (c->step >> 3) - (c->step >> 2);
+        break;
+    case 4:
+    case 5:
+        c->step += (c->step >> 4) - (c->step >> 3);
+        break;
+    case 6:
+        break;
+    case 7:
+        c->step += c->step >> 3;
+        break;
+    case 8:
+        c->step += c->step >> 2;
+        break;
+    case 9:
+        c->step += c->step >> 1;
+        break;
+    case 10:
+        c->step = 2 * c->step - (c->step >> 3);
+        break;
+    case 11:
+        c->step = 2 * c->step + (c->step >> 3);
+        break;
+    case 12:
+        c->step = 2 * c->step + (c->step >> 1) - (c->step >> 3);
+        break;
+    case 13:
+        c->step = 3 * c->step - (c->step >> 2);
+        break;
+    case 14:
+        c->step *= 3;
+        break;
+    case 15:
+    case 16:
+        c->step = (7 * c->step) >> 1;
+        break;
+    }
+
+    if (sign)
+        add = -add;
+
+    c->predictor = av_clip_int16(c->predictor + add);
+    c->step = av_clip(c->step, 1, 1024);
+    return c->predictor;
+}
+
+int ff_adpcm_sanyo_decode(ADPCMChannelStatus *cs, const uint8_t *data, int data_size, int bits_per_coded_sample, int nb_samples, int channels, int16_t **samples_p)
+{
+    int (*expand)(ADPCMChannelStatus *c, int bits);
+    GetBitContext gb;
+
+    switch(bits_per_coded_sample) {
+    case 3: expand = adpcm_sanyo_expand3; break;
+    case 4: expand = adpcm_sanyo_expand4; break;
+    case 5: expand = adpcm_sanyo_expand5; break;
+    }
+
+    init_get_bits8(&gb, data, data_size);
+    for (int i = 0; i < nb_samples; i++)
+        for (int ch = 0; ch < channels; ch++)
+            samples_p[ch][i] = expand(&cs[ch], get_bits(&gb, bits_per_coded_sample));
+
+    align_get_bits(&gb);
+    return get_bits_count(&gb) / 8;
+}
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 4b11ad3a75..082cd22e6a 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -694,6 +694,7 @@ extern const FFCodec ff_adpcm_ms_encoder;
 extern const FFCodec ff_adpcm_ms_decoder;
 extern const FFCodec ff_adpcm_mtaf_decoder;
 extern const FFCodec ff_adpcm_psx_decoder;
+extern const FFCodec ff_adpcm_sanyo_decoder;
 extern const FFCodec ff_adpcm_sbpro_2_decoder;
 extern const FFCodec ff_adpcm_sbpro_3_decoder;
 extern const FFCodec ff_adpcm_sbpro_4_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 5b6aaab88e..dae2296689 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -2619,6 +2619,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Xbox"),
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_ADPCM_SANYO,
+        .type      = AVMEDIA_TYPE_AUDIO,
+        .name      = "adpcm_sanyo",
+        .long_name = NULL_IF_CONFIG_SMALL("ADPCM Sanyo"),
+        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
+    },
 
     /* AMR */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 09dff29886..d00d3fe121 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -425,6 +425,7 @@ enum AVCodecID {
     AV_CODEC_ID_ADPCM_IMA_ACORN,
     AV_CODEC_ID_ADPCM_XMD,
     AV_CODEC_ID_ADPCM_IMA_XBOX,
+    AV_CODEC_ID_ADPCM_SANYO,
 
     /* AMR */
     AV_CODEC_ID_AMR_NB = 0x12000,
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 151563e9f2..3c12c4e6c3 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -570,6 +570,7 @@ const AVCodecTag ff_codec_wav_tags[] = {
     { AV_CODEC_ID_G729,            0x0083 },
     { AV_CODEC_ID_AAC,             0x00ff },
     { AV_CODEC_ID_G723_1,          0x0111 },
+    { AV_CODEC_ID_ADPCM_SANYO,     0x0125 },
     { AV_CODEC_ID_SIPR,            0x0130 },
     { AV_CODEC_ID_ACELP_KELVIN,    0x0135 },
     { AV_CODEC_ID_WMAV1,           0x0160 },
-- 
2.47.2

-- Peter
(A907 E02F A6E5 0CD2 34CD 20D2 6760 79C5 AC40 DD6B)

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

[-- Attachment #2: Type: text/plain, Size: 251 bytes --]

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

             reply	other threads:[~2025-06-25  8:03 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-25  8:02 Peter Ross [this message]
2025-06-25  8:03 ` [FFmpeg-devel] [PATCH 2/2] tests/fate: Sanyo LD-ADPCM test case Peter Ross

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7bb76ae91079c70434e8069230d5e7a4d11a152e.1750838520.git.pross@xvid.org \
    --to=pross@xvid.org \
    --cc=ffmpeg-devel@ffmpeg.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git