Opened 4 years ago

Last modified 4 years 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 Carl Eugen Hoyos)

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 Carl Eugen Hoyos 4 years ago.
ode-to-joy.XA (1.3 MB ) - added by koops 4 years ago.
Stereo file

Change History (15)

comment:1 by koops, 4 years ago

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

comment:2 by koops, 4 years ago

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

comment:3 by koops, 4 years ago

Summary: Raw Electronic Arts ADPCM supportRaw Electronic Arts ADPCM support (.XA)

by Carl Eugen Hoyos, 4 years ago

Attachment: FurElise.XA added

comment:4 by Carl Eugen Hoyos, 4 years ago

Description: modified (diff)
Keywords: electronicarts added
Priority: normalwish
Status: newopen
Version: unspecifiedgit-master

comment:5 by koops, 4 years ago

@cehoyos There is also a decoded .raw file.

comment:6 by koops, 4 years ago

Edit: the file can actually be stereo.

comment:7 by Carl Eugen Hoyos, 4 years ago

Please provide a stereo sample.

comment:8 by koops, 4 years ago

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 by koops, 4 years ago

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 by Carl Eugen Hoyos, 4 years ago

Description: modified (diff)

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

by koops, 4 years ago

Attachment: ode-to-joy.XA added

Stereo file

comment:13 by koops, 4 years ago

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 by koops, 4 years ago

Are you going to implement this feature?

comment:15 by Carl Eugen Hoyos, 4 years ago

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.