Quick way to extract AMR-audio from Android 3GP files
If you need to record audio from microphone of your Android-device to a file, it is better to use MediaRecorder;. On the current Android platforms (we can be sure for 1.6, 1.5 and earlier versions) you can record only to 3GPP AMR-NB file, but not to the raw AMR. If you will try to use MediaRecorder.OutputFormat.RAW_AMR you will get native error that will not come to Java layer. We’re pretty sure that it will be fixed in the future, but what to do now if you need simple raw AMR-NB file without extra 3GP-container?
Simple Google search about how to extract raw AMR audio from 3GPP-file recorded by your Android return us a link to the isobox4j library that can convert between all kinds of 3gp, amr, mp4, etc. files. It is perfect, but it is 400Kb. Do you really need to have such huge library in your project only for converting one type of file to another?
I will show you the easier way that works fine only with Android 3GP-files. What you need to do – just remove 3GP-container and store AMR-data with appropriate header.
3GP-header fields are stored in 32-bits, big-endian format. So, you need correct functions to read 32-bits values from ByteArrayInputStream.
public long readUint32(ByteArrayInputStream bis) { long result = 0; result += ((long) readUInt16(bis)) << 16; result += readUInt16(bis); return result; } public int readUInt16(ByteArrayInputStream bis) { int result = 0; result += bis.read() << 8; result += bis.read(); return result; }
Now you need to skip 3gp-headers. First header is file header (‘ftyp’) and next is MediaData (‘mdat’). I’ve made it according to 3GP and Iso file structure specifications, using classes. Base Box contains two fields: size and type.
class Box { protected long size; protected long type; protected long boxSize = 0; public Box(ByteArrayInputStream bis) { size = readUint32(bis); boxSize += 4; type = readUint32(bis); boxSize += 4; } }
Now classes for Filetype header and MediaData header:
class FileTypeBox extends Box { private static char[] HEADER_TYPE = {'f', 't', 'y', 'p'}; protected long brand; protected long minorVersion; protected long[] compatibleBrands; public FileTypeBox(ByteArrayInputStream bis) { super(bis); brand = readUint32(bis); boxSize += 4; minorVersion = readUint32(bis); boxSize += 4; int remainSize = (int)(size - boxSize); if (remainSize > 0) { compatibleBrands = new long[remainSize / 4]; for (int i = 0; i < compatibleBrands.length; i++) { compatibleBrands[i] = readUint32(bis); } } } }
class MediaDataBox extends Box { private static char[] HEADER_TYPE = {'m', 'd', 'a', 't'}; protected byte[] data; public MediaDataBox(ByteArrayInputStream bis) { super(bis); // you can read data[] here, but we need only size } public int getDataLength() { return (int)(size - boxSize); } }
I add HEADER_TYPE to each Box-class. You can add checking that class field type has the appropriate HEADER_TYPE value just to be sure that you read and parse correct file.
So, we’re ready to convert 3GP to AMR. 3GP-structure is as follows:
1. FileType header
2. MediaDataHeader. Data inside it is raw AMR data.
3. MovieData box (‘moov’ type) that presents, but is empty and we don’t need to copy it to destination AMR-file.
Converting steps is as follows:
1. Skip FileType header
2. Skip MediaData Header size and type fields, get raw AMR data only.
3. Create new file
4. Add AMR-header magic number – “#!AMR\n” (according to RFC-3267)
5. Copy all raw AMR data to file.
That’s it!
// #!AMR\n private static byte[] AMR_MAGIC_HEADER = {0x23, 0x21, 0x41, 0x4d, 0x52, 0x0a}; public byte[] convert3gpDataToAmr(byte[] data) { if (data == null) { return null; } ByteArrayInputStream bis = new ByteArrayInputStream(data); // read FileTypeHeader FileTypeBox ftypHeader = new FileTypeBox(bis); // You can check if it is correct here // read MediaDataHeader MediaDataBox mdatHeader = new MediaDataBox(bis); // You can check if it is correct here int rawAmrDataLength = mdatHeader.getDataLength(); int fullAmrDataLength = AMR_MAGIC_HEADER.length + rawAmrDataLength; byte[] amrData = new byte[fullAmrDataLength]; System.arraycopy(AMR_MAGIC_HEADER, 0, amrData, 0, AMR_MAGIC_HEADER.length); bis.read(amrData, AMR_MAGIC_HEADER.length, rawAmrDataLength); return amrData; }
Enjoy!


brilliant..thanks so much…
Can you also give me pointers on how to convert the AMR data to WAV?
And How to increase the Gain/volume/amplitude of AMR data..like peak normalisation
What is the reason to convert AMR to WAV? You can use another way to record audio on Android via AudioRecord class. It writes data in raw PCM format that can be simply converted to WAV.
The same thing is for audio processing, it is better to use PCM for it because AMR uses lossy compression method for audio encoding.
I dont really need to convert AMR to WAV..but i need to manipulate the volume of 3gp/amr files…the input for my application is a 3gp/amr file and this file was recorded with a poor mic or maybe from a long distance…
I see. To add any sound processing, I think, you should convert AMR to PCM and then change amplitudes. But it can cost you a lot of memory, because PCM is about 5 times bigger than AMR.
Do you have any code or links that can convert AMR to PCM? I’m doing this on the server once I get the AMR from the device, so memory is not a concern.
hello I need to do the reverse process of amr to 3gp how to add headers Help me please
What does “<< 16;” mean? I am getting an error with that
LOL) you answered your question:)
this is fantastic
For the audio processing newbie, could you explain how to convert a file of another format (say, mp3?) to wav? Is it as simple as this process? Basically I want to get sample values from a user’s mp3.