Opened 4 weeks ago

Last modified 2 weeks ago

#6690 open enhancement

Raw Electronic Arts ADPCM support (.XA)

Reported by: koops Owned by:
Priority: wish Component: avformat
Version: git-master Keywords: electronicarts
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description (last modified by cehoyos)

Summary of the bug:
How to reproduce:

$ ffmpeg -i sound.XA sound.wav
sound.XA: Invalid data found when processing input

I would like support for headerless Electronic Arts ADPCM files to be added.
These files are common in some Electronic Arts games, like Harry Potter and the Chamber of Secrets and Brother Bear.

The format is similar to adpcm_ea_r1, but the file is always mono, there is no channels header. ADPCM history is always initialized to [0, 0].
File size is always a multiple of 15, there is no raw sample mode (0xEE).

Sample rate is 22050Hz by default.

Files can be encoded into this format using sx.exe:

> sx -raw -eaxa_blk sample.wav -=sample.XA

I attached a test case, a XA file and the corresponding decoded raw file.

This is decoder code in C# (I release it under CC0):

using System;
using System.IO;
using System.Text;

namespace KoopsAudioDecoder
{
  class Program
  {
    static int coeff1, coeff2, shift, adpcmHistory1 = 0, adpcmHistory2 = 0;

    static readonly int[] EA_XA_TABLE = new int[] {
        0,    0,
      240,    0,
      460, -208,
      392, -220,
    };

    static void DecodeSingleFrame(BinaryReader stream, BinaryWriter outbuf)
    {
      int frameInfo = stream.ReadByte();

      coeff1 = EA_XA_TABLE[((frameInfo >> 4) & 15) * 2];
      coeff2 = EA_XA_TABLE[((frameInfo >> 4) & 15) * 2 + 1];
      shift = (frameInfo & 15) + 8;

      for (int i = 0; i < 14; i++)
      {
        int sample_byte = stream.ReadByte();

        int[] nibbles = { sample_byte >> 4, sample_byte & 15 };

        foreach (int nibble in nibbles)
        {
          int sample = GetSample(nibble);

          outbuf.Write(Clamp16(sample));
        }
      }
    }

    private static int GetSample(int nibble)
    {
      int sample = ((nibble << 28 >> shift) + (coeff1 * adpcmHistory1) + (coeff2 * adpcmHistory2)) >> 8;

      adpcmHistory2 = adpcmHistory1;
      adpcmHistory1 = sample;

      return sample;
    }

    static private short Clamp16(int sample)
    {
      if (sample > 32767)
      {
        return 32767;
      }

      if (sample < -32768)
      {
        return -32768;
      }

      return (short) sample;
    }

    static int Main(string[] args)
    {
      if (args.Length != 1)
      {
        Console.WriteLine("This program takes only one argument, input file");
        return 1;
      }

      string inputFileName = args[0], outputFileName = Path.ChangeExtension(inputFileName, "raw");
      Stream inputFile = File.OpenRead(inputFileName), outputFile = File.OpenWrite(outputFileName);
      BinaryReader inputFileReader = new BinaryReader(inputFile);
      BinaryWriter outputFileWriter = new BinaryWriter(outputFile);

      while (inputFile.Length - inputFile.Position >= 15)
      {
        DecodeSingleFrame(inputFileReader, outputFileWriter);
      }

      if (inputFile.Length - inputFile.Position > 0)
      {
        Console.WriteLine("File has {0} spare bytes.", inputFile.Length - inputFile.Position);
      }

      return 0;
    }
  }
}

Attachments (2)

FurElise.XA (2.0 MB) - added by cehoyos 4 weeks ago.
ode-to-joy.XA (1.3 MB) - added by koops 2 weeks ago.
Stereo file

Change History (15)

comment:1 Changed 4 weeks ago by koops

Test case files are uploaded to https://streams.videolan.org/upload/.

comment:2 Changed 4 weeks ago by koops

Test case files are uploaded to https://streams.videolan.org/upload/.

comment:3 Changed 4 weeks ago by koops

  • Summary changed from Raw Electronic Arts ADPCM support to Raw Electronic Arts ADPCM support (.XA)

Changed 4 weeks ago by cehoyos

comment:4 Changed 4 weeks ago by cehoyos

  • Description modified (diff)
  • Keywords electronicarts added
  • Priority changed from normal to wish
  • Status changed from new to open
  • Version changed from unspecified to git-master

comment:5 Changed 4 weeks ago by koops

@cehoyos There is also a decoded .raw file.

comment:6 Changed 4 weeks ago by koops

Edit: the file can actually be stereo.

comment:7 Changed 4 weeks ago by cehoyos

Please provide a stereo sample.

comment:8 Changed 4 weeks ago by koops

I uploaded it to https://streams.videolan.org/upload/. It's the one without "fixed" in description, I accidentally created a stereo sample, while I wanted mono.

comment:9 Changed 4 weeks ago by koops

You can detect this format by file size being a multiple of 15 and 0th, 15th, 30th and so on bytes being less than 0x40.

comment:12 Changed 3 weeks ago by cehoyos

  • Description modified (diff)

The stereo file unfortunately never appeared, please attach it here.

Changed 2 weeks ago by koops

Stereo file

comment:13 Changed 2 weeks ago by koops

The stereo file is two mono files going after each other, not an interleaved file. You can only keep mono support if you like.

comment:14 Changed 2 weeks ago by koops

Are you going to implement this feature?

comment:15 Changed 2 weeks ago by cehoyos

It seems to me you are the right person to do it, given you already implemented a decoder.

Note: See TracTickets for help on using tickets.