---
title: "Decode AMR in C#: .amr, .awb and 3GP to WAV"
url: https://recursiveai.org/oneaudio/cookbook/decode-amr/
description: "Convert .amr, .awb and 3GP voice recordings to WAV in C#, or decode AMR and AMR-WB frames one at a time with lost-frame concealment."
version: "1.4.0"
---

# Decode AMR in C#: .amr, .awb and 3GP to WAV

oneaudio decodes AMR (narrowband, 8 kHz) and AMR-WB (wideband, 16 kHz) from `.amr`, `.awb` and 3GP files, the formats of phone voice memos and MMS, in managed code. The files here are three-second synthetic test files from the oneaudio repository. Their frames hold pseudo-random bytes rather than speech, so they decode to noise, but they are valid AMR.

```shell
dotnet new console -o DecodeAmr && cd DecodeAmr
dotnet add package RecursiveAI.OneAudio --version 1.4.0
t=https://raw.githubusercontent.com/recursive-ai-oss/oneaudio-csharp/v1.4.0/testdata
curl -L -o memo.amr $t/containers/synthetic-nb.amr
curl -L -o memo.awb $t/containers/synthetic-wb.awb
curl -L -o memo.3gp $t/containers/amrnb.3gp
```

Put this in `Program.cs` and run `dotnet run`:

```csharp
using OneAudio;
using OneAudio.Amr;

// A kit that checks for MP4 and AMR before MP3 (see "If Audio.Open fails").
var kit = new AudioKit(new AudioCodecRegistry()
    .AddWav().AddMp4().AddAmr().AddAac().AddMp3());

foreach (string path in new[] { "memo.amr", "memo.awb", "memo.3gp" })
{
    using IAudioDecoder decoder = kit.Open(path);
    AudioFormat f = decoder.Format;
    Console.WriteLine($"{path}: {f.SampleRate} Hz, {f.Channels} ch, " +
                      $"{decoder.LengthFrames / (double)f.SampleRate:F2} s");
}

// AMR to WAV.
TranscodeResult wav = kit.Transcode("memo.amr", "memo.wav");
Console.WriteLine($"memo.wav: {wav.Frames} frames, " +
                  $"{new FileInfo("memo.wav").Length:N0} bytes");

// At the rate and channels your app mixes at.
var at48k = new LoadOptions { SampleRate = 48000, Channels = 2 };
AudioClip memo = kit.Load("memo.awb", at48k);
Console.WriteLine($"memo.awb at 48 kHz stereo: {memo.Frames} frames");

// Frames from a network stream go straight to AmrNbDecoder: a table-of-contents
// byte plus the speech bits, 20 ms each. Here they come from memo.amr.
byte[] file = File.ReadAllBytes("memo.amr");
var nb = new AmrNbDecoder();
short[] pcm = new short[AmrNbDecoder.SamplesPerFrame];
int offset = 6, decoded = 0;                     // skip the "#!AMR\n" magic
while (offset < file.Length)
{
    int type = AmrFrameTable.FrameType(file[offset]);
    int size = 1 + AmrFrameTable.PayloadBytes(false, type);
    if (decoded == 50)
        nb.DecodeLost(pcm);                      // a lost packet, concealed
    else
        nb.Decode(file.AsSpan(offset, size), pcm);
    offset += size;
    decoded++;
}
Console.WriteLine($"AmrNbDecoder: {decoded} frames, one concealed");
```

```text
memo.amr: 8000 Hz, 1 ch, 3.00 s
memo.awb: 16000 Hz, 1 ch, 3.00 s
memo.3gp: 8000 Hz, 1 ch, 3.00 s
memo.wav: 24000 frames, 48,080 bytes
memo.awb at 48 kHz stereo: 144000 frames
AmrNbDecoder: 150 frames, one concealed
```

## If Audio.Open fails

In 1.4.0, `Audio.Open` and `Audio.Load` check for MP3 before MP4 and AMR, and the MP3 check can claim some `.amr` and `.3gp` files: they throw `AudioFormatException: No MPEG audio frame found.` The kit in the program checks MP4 and AMR first, and has the same `Open`, `Load` and `Transcode`. A speech-only app can register just these formats, which also keeps the other decoders out of a trimmed build.

## Files and frames

- AMR decodes to 8 kHz mono and AMR-WB to 16 kHz mono. `.amr` and `.awb` files (RFC 4867 storage format) and 3GP files with AMR or AMR-WB tracks open like any other format.
- Raw frames go through `AmrNbDecoder` or `AmrWbDecoder`, one frame per call, as files store them. `AmrFrameTable` gives a frame’s size from its type. `Decode` writes 16-bit or float samples.
- For a lost frame call `DecodeLost`. A frame marked damaged is concealed too, and “no data” frames are bridged with comfort noise.

## Limits

- All eight AMR modes and all nine AMR-WB modes decode, bit-exact against the 3GPP reference decoders on all 573 TS 26.074 and 221 TS 26.174 test sequences.
- Seeking is exact in position, but the samples after a seek only come close to a decode from the start. For bit-exact output, read forward from the start.
- oneaudio doesn’t write AMR.

Music in M4A and AAC files: [Decode AAC and M4A](https://recursiveai.org/oneaudio/cookbook/decode-aac/).
