Task 416: .MPA File Format

Task 416: .MPA File Format

The .MPA file format is an audio file format for raw MPEG-1 or MPEG-2 Audio Layer II streams, as defined in the ISO/IEC 11172-3 and ISO/IEC 13818-3 standards.

  1. The properties intrinsic to the .MPA file format are the following frame header fields (the file is a sequence of audio frames, each with this structure, and no overall file header beyond the concatenated frames):
  • Sync word (11 bits): Always 11111111111 to mark the start of a frame.
  • Version (2 bits): The MPEG version (11 for MPEG-1, 10 for MPEG-2, 00 for MPEG-2.5, 01 reserved).
  • Layer (2 bits): The audio layer (10 for Layer II).
  • Protection bit (1 bit): 0 if the frame has a 16-bit CRC, 1 if no CRC.
  • Bitrate index (4 bits): Index to the bitrate (values 0-15 mapping to specific kbit/s rates, depending on version and layer; e.g., for MPEG-1 Layer II, 0001 = 32 kbit/s, 1010 = 192 kbit/s).
  • Sampling rate index (2 bits): Index to the sampling rate (depending on version; e.g., for MPEG-1, 00 = 44.1 kHz, 01 = 48 kHz, 10 = 32 kHz, 11 reserved).
  • Padding bit (1 bit): 0 if no padding, 1 if an extra byte is added to the frame.
  • Private bit (1 bit): Reserved for application-specific use (0 or 1).
  • Channel mode (2 bits): The channel configuration (00 = stereo, 01 = joint stereo, 10 = dual channel, 11 = mono).
  • Mode extension (2 bits): Additional details for joint stereo mode (specifies bands for intensity stereo or MS stereo).
  • Copyright bit (1 bit): 0 if not copyrighted, 1 if copyrighted.
  • Original bit (1 bit): 0 if copy, 1 if original.
  • Emphasis (2 bits): Pre-emphasis applied (00 = none, 01 = 50/15 µs, 10 reserved, 11 = CCITT J.17).

Derived properties per frame include the actual bitrate (kbit/s), sampling rate (Hz), number of channels (1 or 2), and frame length (bytes, calculated as (144 * bitrate) / sampling_rate + padding for Layer II).

  1. Two direct download links for .MPA format files (note: the format is identical to .MP2 files, which are more commonly available for samples; these can be renamed to .MPA if needed):
  1. Here is the embedded HTML/JavaScript for a Ghost blog post that allows drag-and-drop of a .MPA file and dumps the properties to the screen (it parses the first frame header and displays the properties; assumes the file is valid and starts with a frame):
Drag and drop .MPA file here
  1. Python class:
import struct
import os

class MPAParsing:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}
        self.data = None

    def decode_read(self):
        with open(self.filepath, 'rb') as f:
            self.data = f.read()
        # Find first sync
        offset = 0
        while offset < len(self.data) - 4:
            if self.data[offset] == 0xFF and (self.data[offset+1] & 0xE0) == 0xE0:
                break
            offset += 1
        if offset >= len(self.data) - 4:
            print("No valid MPA frame found.")
            return
        header = struct.unpack('>I', self.data[offset:offset+4])[0]
        version_index = (header >> 19) & 3
        layer_index = (header >> 17) & 3
        crc = (header >> 16) & 1
        bitrate_index = (header >> 12) & 15
        sampling_index = (header >> 10) & 3
        padding = (header >> 9) & 1
        private_bit = (header >> 8) & 1
        channel_index = (header >> 6) & 3
        mode_ext = (header >> 4) & 3
        copyright = (header >> 3) & 1
        original = (header >> 2) & 1
        emphasis = header & 3
        self.properties = {
            'Version index': version_index,
            'Layer index': layer_index,
            'Protection bit': crc,
            'Bitrate index': bitrate_index,
            'Sampling rate index': sampling_index,
            'Padding bit': padding,
            'Private bit': private_bit,
            'Channel mode index': channel_index,
            'Mode extension': mode_ext,
            'Copyright bit': copyright,
            'Original bit': original,
            'Emphasis': emphasis
        }

    def print_properties(self):
        if not self.properties:
            print("No properties decoded.")
            return
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write(self, new_filepath):
        if not self.data:
            # Create a dummy file if no data
            # Use example values for MPEG-1 Layer II, 192 kbit/s, 44.1 kHz, stereo, no padding, no CRC
            version = 3  # 11
            layer = 2  # 10
            crc = 1  # no CRC
            bitrate_index = 10  # 192 kbit/s for M1L2
            sampling_index = 0  # 44.1
            padding = 0
            private = 0
            channel = 0  # stereo
            mode_ext = 0
            copyright = 0
            original = 1
            emphasis = 0
            header_int = (0xFFF << 21) | (version << 19) | (layer << 17) | (crc << 16) | (bitrate_index << 12) | (sampling_index << 10) | (padding << 9) | (private << 8) | (channel << 6) | (mode_ext << 4) | (copyright << 3) | (original << 2) | emphasis
            header = struct.pack('>I', header_int)
            bitrate = 192000  # bps
            sampling = 44100
            frame_size = (144 * bitrate // sampling) + padding
            dummy_data = b'\x00' * (frame_size - 4)
            self.data = header + dummy_data
        with open(new_filepath, 'wb') as f:
            f.write(self.data)

# Example usage:
# parser = MPAParsing('example.mpa')
# parser.decode_read()
# parser.print_properties()
# parser.write('new.mpa')
  1. Java class:
import java.io.*;
import java.nio.*;

public class MPAParsing {
    private String filepath;
    private byte[] data;
    private int[] properties = new int[12]; // For the 12 fields

    public MPAParsing(String filepath) {
        this.filepath = filepath;
    }

    public void decodeRead() throws IOException {
        File file = new File(filepath);
        data = new byte[(int) file.length()];
        try (FileInputStream fis = new FileInputStream(file)) {
            fis.read(data);
        }
        // Find first sync
        int offset = 0;
        while (offset < data.length - 4) {
            if ((data[offset] & 0xFF) == 0xFF && (data[offset + 1] & 0xE0) == 0xE0) {
                break;
            }
            offset++;
        }
        if (offset >= data.length - 4) {
            System.out.println("No valid MPA frame found.");
            return;
        }
        ByteBuffer bb = ByteBuffer.wrap(data, offset, 4).order(ByteOrder.BIG_ENDIAN);
        int header = bb.getInt();
        properties[0] = (header >> 19) & 3; // version
        properties[1] = (header >> 17) & 3; // layer
        properties[2] = (header >> 16) & 1; // crc
        properties[3] = (header >> 12) & 15; // bitrate index
        properties[4] = (header >> 10) & 3; // sampling index
        properties[5] = (header >> 9) & 1; // padding
        properties[6] = (header >> 8) & 1; // private
        properties[7] = (header >> 6) & 3; // channel
        properties[8] = (header >> 4) & 3; // mode ext
        properties[9] = (header >> 3) & 1; // copyright
        properties[10] = (header >> 2) & 1; // original
        properties[11] = header & 3; // emphasis
    }

    public void printProperties() {
        String[] keys = {"Version index", "Layer index", "Protection bit", "Bitrate index", "Sampling rate index", "Padding bit", "Private bit", "Channel mode index", "Mode extension", "Copyright bit", "Original bit", "Emphasis"};
        for (int i = 0; i < keys.length; i++) {
            System.out.println(keys[i] + ": " + properties[i]);
        }
    }

    public void write(String newFilepath) throws IOException {
        if (data == null) {
            // Create dummy
            int version = 3;
            int layer = 2;
            int crc = 1;
            int bitrateIndex = 10;
            int samplingIndex = 0;
            int padding = 0;
            int privateBit = 0;
            int channel = 0;
            int modeExt = 0;
            int copyright = 0;
            int original = 1;
            int emphasis = 0;
            int header = (0xFFF << 21) | (version << 19) | (layer << 17) | (crc << 16) | (bitrateIndex << 12) | (samplingIndex << 10) | (padding << 9) | (privateBit << 8) | (channel << 6) | (modeExt << 4) | (copyright << 3) | (original << 2) | emphasis;
            ByteBuffer bb = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
            bb.putInt(header);
            byte[] headerBytes = bb.array();
            int bitrate = 192000;
            int sampling = 44100;
            int frameSize = (144 * bitrate / sampling) + padding;
            data = new byte[frameSize];
            System.arraycopy(headerBytes, 0, data, 0, 4);
            // Dummy data already zeros
        }
        try (FileOutputStream fos = new FileOutputStream(newFilepath)) {
            fos.write(data);
        }
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     MPAParsing parser = new MPAParsing("example.mpa");
    //     parser.decodeRead();
    //     parser.printProperties();
    //     parser.write("new.mpa");
    // }
}
  1. JavaScript class:
class MPAParsing {
  constructor(filepath) {
    this.filepath = filepath;
    this.properties = {};
    this.data = null;
  }

  async decodeRead() {
    // Note: In Node.js, use fs module
    const fs = require('fs');
    this.data = fs.readFileSync(this.filepath);
    let offset = 0;
    while (offset < this.data.length - 4) {
      if (this.data[offset] === 0xFF && (this.data[offset + 1] & 0xE0) === 0xE0) {
        break;
      }
      offset++;
    }
    if (offset >= this.data.length - 4) {
      console.log('No valid MPA frame found.');
      return;
    }
    const header = (this.data[offset] << 24) | (this.data[offset + 1] << 16) | (this.data[offset + 2] << 8) | this.data[offset + 3];
    this.properties = {
      versionIndex: (header >> 19) & 3,
      layerIndex: (header >> 17) & 3,
      protectionBit: (header >> 16) & 1,
      bitrateIndex: (header >> 12) & 15,
      samplingIndex: (header >> 10) & 3,
      paddingBit: (header >> 9) & 1,
      privateBit: (header >> 8) & 1,
      channelModeIndex: (header >> 6) & 3,
      modeExtension: (header >> 4) & 3,
      copyrightBit: (header >> 3) & 1,
      originalBit: (header >> 2) & 1,
      emphasis: header & 3
    };
  }

  printProperties() {
    if (Object.keys(this.properties).length === 0) {
      console.log('No properties decoded.');
      return;
    }
    for (const [key, value] of Object.entries(this.properties)) {
      console.log(`${key}: ${value}`);
    }
  }

  write(newFilepath) {
    if (!this.data) {
      // Create dummy
      const version = 3;
      const layer = 2;
      const crc = 1;
      const bitrateIndex = 10;
      const samplingIndex = 0;
      const padding = 0;
      const privateBit = 0;
      const channel = 0;
      const modeExt = 0;
      const copyright = 0;
      const original = 1;
      const emphasis = 0;
      const header = (0xFFF << 21) | (version << 19) | (layer << 17) | (crc << 16) | (bitrateIndex << 12) | (samplingIndex << 10) | (padding << 9) | (privateBit << 8) | (channel << 6) | (modeExt << 4) | (copyright << 3) | (original << 2) | emphasis;
      const headerBuffer = new Uint32Array([header]);
      const headerBytes = new Uint8Array(headerBuffer.buffer).reverse(); // Big endian
      const bitrate = 192000;
      const sampling = 44100;
      const frameSize = (144 * bitrate / sampling) + padding;
      this.data = new Uint8Array(frameSize);
      this.data.set(headerBytes, 0);
      // Dummy data zeros
    }
    const fs = require('fs');
    fs.writeFileSync(newFilepath, this.data);
  }
}

// Example usage:
// const parser = new MPAParsing('example.mpa');
// await parser.decodeRead();
// parser.printProperties();
// parser.write('new.mpa');
  1. C class (struct-based, with functions; compile with std=c99 or later):
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

typedef struct {
    char* filepath;
    uint8_t* data;
    size_t size;
    int properties[12];
} MPAParsing;

MPAParsing* mpa_create(const char* filepath) {
    MPAParsing* parser = malloc(sizeof(MPAParsing));
    parser->filepath = strdup(filepath);
    parser->data = NULL;
    parser->size = 0;
    memset(parser->properties, 0, sizeof(parser->properties));
    return parser;
}

void mpa_decode_read(MPAParsing* parser) {
    FILE* f = fopen(parser->filepath, "rb");
    if (!f) return;
    fseek(f, 0, SEEK_END);
    parser->size = ftell(f);
    fseek(f, 0, SEEK_SET);
    parser->data = malloc(parser->size);
    fread(parser->data, 1, parser->size, f);
    fclose(f);
    size_t offset = 0;
    while (offset < parser->size - 4) {
        if (parser->data[offset] == 0xFF && (parser->data[offset + 1] & 0xE0) == 0xE0) {
            break;
        }
        offset++;
    }
    if (offset >= parser->size - 4) {
        printf("No valid MPA frame found.\n");
        return;
    }
    uint32_t header = (parser->data[offset] << 24) | (parser->data[offset + 1] << 16) | (parser->data[offset + 2] << 8) | parser->data[offset + 3];
    parser->properties[0] = (header >> 19) & 3;
    parser->properties[1] = (header >> 17) & 3;
    parser->properties[2] = (header >> 16) & 1;
    parser->properties[3] = (header >> 12) & 15;
    parser->properties[4] = (header >> 10) & 3;
    parser->properties[5] = (header >> 9) & 1;
    parser->properties[6] = (header >> 8) & 1;
    parser->properties[7] = (header >> 6) & 3;
    parser->properties[8] = (header >> 4) & 3;
    parser->properties[9] = (header >> 3) & 1;
    parser->properties[10] = (header >> 2) & 1;
    parser->properties[11] = header & 3;
}

void mpa_print_properties(MPAParsing* parser) {
    const char* keys[12] = {"Version index", "Layer index", "Protection bit", "Bitrate index", "Sampling rate index", "Padding bit", "Private bit", "Channel mode index", "Mode extension", "Copyright bit", "Original bit", "Emphasis"};
    for (int i = 0; i < 12; i++) {
        printf("%s: %d\n", keys[i], parser->properties[i]);
    }
}

void mpa_write(MPAParsing* parser, const char* new_filepath) {
    if (!parser->data) {
        // Create dummy
        uint32_t version = 3;
        uint32_t layer = 2;
        uint32_t crc = 1;
        uint32_t bitrate_index = 10;
        uint32_t sampling_index = 0;
        uint32_t padding = 0;
        uint32_t private_bit = 0;
        uint32_t channel = 0;
        uint32_t mode_ext = 0;
        uint32_t copyright = 0;
        uint32_t original = 1;
        uint32_t emphasis = 0;
        uint32_t header = (0xFFF << 21) | (version << 19) | (layer << 17) | (crc << 16) | (bitrate_index << 12) | (sampling_index << 10) | (padding << 9) | (private_bit << 8) | (channel << 6) | (mode_ext << 4) | (copyright << 3) | (original << 2) | emphasis;
        uint32_t bitrate = 192000;
        uint32_t sampling = 44100;
        uint32_t frame_size = (144 * bitrate / sampling) + padding;
        parser->data = calloc(frame_size, 1);
        parser->data[0] = (header >> 24) & 0xFF;
        parser->data[1] = (header >> 16) & 0xFF;
        parser->data[2] = (header >> 8) & 0xFF;
        parser->data[3] = header & 0xFF;
        parser->size = frame_size;
    }
    FILE* f = fopen(new_filepath, "wb");
    if (f) {
        fwrite(parser->data, 1, parser->size, f);
        fclose(f);
    }
}

void mpa_destroy(MPAParsing* parser) {
    free(parser->filepath);
    free(parser->data);
    free(parser);
}

// Example usage:
// int main() {
//     MPAParsing* parser = mpa_create("example.mpa");
//     mpa_decode_read(parser);
//     mpa_print_properties(parser);
//     mpa_write(parser, "new.mpa");
//     mpa_destroy(parser);
//     return 0;
// }