Task 014: .ADT File Format
Task 014: .ADT File Format
1. Properties of the .ADT File Format
The .ADT file format refers to the Audio Data Transport Stream (ADTS) audio file, a container for AAC-encoded audio data. It is a binary format consisting of a series of frames, each with a header followed by raw audio data. The properties intrinsic to this format are the fields within the ADTS header, which are consistent across frames and define the audio stream's characteristics. These properties are extracted from the 56-bit (7-byte) fixed header, plus an optional 16-bit CRC if protection is enabled. The key properties are as follows:
- Syncword (12 bits): A fixed value of 0xFFF to mark the start of a frame.
- ID (1 bit): MPEG version indicator (0 for MPEG-4, 1 for MPEG-2).
- Layer (2 bits): Always 0 for AAC.
- Protection Absent (1 bit): Indicates if a CRC is present (1 = no CRC, 0 = CRC present).
- Profile (2 bits): AAC profile type (0 = Main, 1 = Low Complexity, 2 = Scalable Sampling Rate, 3 = reserved).
- Sampling Frequency Index (4 bits): Index to the sampling rate (e.g., 0 = 96000 Hz, 3 = 48000 Hz, 12 = 8000 Hz).
- Private Bit (1 bit): Reserved for private use.
- Channel Configuration (3 bits): Number of channels (e.g., 1 = mono, 2 = stereo, up to 7 = 7.1 channels).
- Original/Copy (1 bit): Indicates if the audio is an original (1) or copy (0).
- Home (1 bit): Reserved for home use indicator.
- Copyright Identification Bit (1 bit): Part of copyright management.
- Copyright Identification Start (1 bit): Indicates start of copyright ID sequence.
- Frame Length (13 bits): Total length of the frame in bytes, including header and CRC (if present).
- Buffer Fullness (11 bits): Indicates decoder buffer state for constant bitrate streams.
- Number of Raw Data Blocks in Frame (2 bits): Number of AAC raw data blocks minus 1 (0 = 1 block, up to 3 = 4 blocks).
- CRC (16 bits, optional): Cyclic redundancy check for error detection, present only if Protection Absent = 0.
These properties are repeated for each frame in the file, allowing the stream to be self-synchronizing and recoverable from errors.
2. Direct Download Links for .ADT Files
The ADTS format is commonly distributed with the .aac extension but is identical in structure to .adt files (as per format specifications). The following are direct download links to sample ADTS audio files, which can be renamed to .adt without loss of functionality:
- https://filesamples.com/samples/audio/aac/sample1.aac (Size: 1.2 MB, Stereo, 44.1 kHz sampling)
- https://filesamples.com/samples/audio/aac/sample2.aac (Size: 2.5 MB, Stereo, 48 kHz sampling)
3. HTML/JavaScript for Drag-and-Drop .ADT File Dump
The following is a complete, self-contained HTML page with embedded JavaScript that allows users to drag and drop a .ADT (ADTS) file. Upon dropping, it reads the file, parses each frame's header, and dumps all properties to the screen in a structured format. This can be embedded in a Ghost blog or any HTML context.
4. Python Class for .ADT File Handling
The following Python class can open a .ADT file, decode its frames, read and print the properties, and write a modified version (e.g., copying the file while preserving structure).
import struct
import sys
class ADTParser:
def __init__(self, filepath):
self.filepath = filepath
self.frames = []
def read(self):
with open(self.filepath, 'rb') as f:
data = f.read()
offset = 0
while offset + 7 < len(data):
syncword = (data[offset] << 4) | (data[offset + 1] >> 4)
if syncword != 0xFFF:
offset += 1
continue
id = (data[offset + 1] >> 3) & 0x01
layer = (data[offset + 1] >> 1) & 0x03
protection_absent = data[offset + 1] & 0x01
profile = (data[offset + 2] >> 6) & 0x03
sampling_freq_index = (data[offset + 2] >> 2) & 0x0F
private_bit = (data[offset + 2] >> 1) & 0x01
channel_config = ((data[offset + 2] & 0x01) << 2) | ((data[offset + 3] >> 6) & 0x03)
original_copy = (data[offset + 3] >> 5) & 0x01
home = (data[offset + 3] >> 4) & 0x01
copyright_id_bit = (data[offset + 3] >> 3) & 0x01
copyright_id_start = (data[offset + 3] >> 2) & 0x01
frame_length = ((data[offset + 3] & 0x03) << 11) | (data[offset + 4] << 3) | ((data[offset + 5] >> 5) & 0x07)
buffer_fullness = ((data[offset + 5] & 0x1F) << 6) | ((data[offset + 6] >> 2) & 0x3F)
num_raw_blocks = data[offset + 6] & 0x03
header_size = 7 if protection_absent else 9
crc = struct.unpack_from('>H', data, offset + 7)[0] if not protection_absent and offset + 9 <= len(data) else None
frame_data = data[offset:offset + frame_length]
self.frames.append({
'syncword': 0xFFF,
'id': id,
'layer': layer,
'protection_absent': protection_absent,
'profile': profile,
'sampling_freq_index': sampling_freq_index,
'private_bit': private_bit,
'channel_config': channel_config,
'original_copy': original_copy,
'home': home,
'copyright_id_bit': copyright_id_bit,
'copyright_id_start': copyright_id_start,
'frame_length': frame_length,
'buffer_fullness': buffer_fullness,
'num_raw_blocks': num_raw_blocks + 1,
'crc': crc,
'data': frame_data
})
if frame_length < header_size:
break
offset += frame_length
def print_properties(self):
for i, frame in enumerate(self.frames, 1):
print(f"Frame {i}:")
for key, value in frame.items():
if key != 'data':
print(f" {key.capitalize().replace('_', ' ')}: {value}")
print()
def write(self, output_path):
with open(output_path, 'wb') as f:
for frame in self.frames:
f.write(frame['data'])
# Example usage
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python adt_parser.py <file.adt>")
sys.exit(1)
parser = ADTParser(sys.argv[1])
parser.read()
parser.print_properties()
parser.write("output.adt") # Writes a copy
5. Java Class for .ADT File Handling
The following Java class can open a .ADT file, decode its frames, read and print the properties, and write a modified version.
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class ADTParser {
private String filepath;
private byte[] data;
public ADTParser(String filepath) {
this.filepath = filepath;
}
public void read() throws IOException {
try (FileInputStream fis = new FileInputStream(filepath)) {
data = fis.readAllBytes();
}
}
public void printProperties() {
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
int offset = 0;
int frameCount = 0;
while (offset + 7 < data.length) {
int syncword = (bb.getShort(offset) >> 4) & 0xFFF;
if (syncword != 0xFFF) {
offset++;
continue;
}
frameCount++;
byte byte1 = data[offset + 1];
byte byte2 = data[offset + 2];
byte byte3 = data[offset + 3];
byte byte4 = data[offset + 4];
byte byte5 = data[offset + 5];
byte byte6 = data[offset + 6];
int id = (byte1 >> 3) & 0x01;
int layer = (byte1 >> 1) & 0x03;
int protectionAbsent = byte1 & 0x01;
int profile = (byte2 >> 6) & 0x03;
int samplingFreqIndex = (byte2 >> 2) & 0x0F;
int privateBit = (byte2 >> 1) & 0x01;
int channelConfig = ((byte2 & 0x01) << 2) | ((byte3 >> 6) & 0x03);
int originalCopy = (byte3 >> 5) & 0x01;
int home = (byte3 >> 4) & 0x01;
int copyrightIdBit = (byte3 >> 3) & 0x01;
int copyrightIdStart = (byte3 >> 2) & 0x01;
int frameLength = ((byte3 & 0x03) << 11) | (byte4 << 3) | ((byte5 >> 5) & 0x07);
int bufferFullness = ((byte5 & 0x1F) << 6) | ((byte6 >> 2) & 0x3F);
int numRawBlocks = byte6 & 0x03;
int headerSize = protectionAbsent == 1 ? 7 : 9;
Short crc = (protectionAbsent == 0 && offset + 9 <= data.length) ? bb.getShort(offset + 7) : null;
System.out.println("Frame " + frameCount + ":");
System.out.println(" Syncword: 0xFFF");
System.out.println(" ID: " + id);
System.out.println(" Layer: " + layer);
System.out.println(" Protection Absent: " + protectionAbsent);
System.out.println(" Profile: " + profile);
System.out.println(" Sampling Frequency Index: " + samplingFreqIndex);
System.out.println(" Private Bit: " + privateBit);
System.out.println(" Channel Configuration: " + channelConfig);
System.out.println(" Original/Copy: " + originalCopy);
System.out.println(" Home: " + home);
System.out.println(" Copyright Identification Bit: " + copyrightIdBit);
System.out.println(" Copyright Identification Start: " + copyrightIdStart);
System.out.println(" Frame Length: " + frameLength);
System.out.println(" Buffer Fullness: " + bufferFullness);
System.out.println(" Number of Raw Data Blocks: " + (numRawBlocks + 1));
if (crc != null) {
System.out.println(" CRC: 0x" + Integer.toHexString(crc & 0xFFFF).toUpperCase());
}
System.out.println();
if (frameLength < headerSize) break;
offset += frameLength;
}
}
public void write(String outputPath) throws IOException {
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
fos.write(data);
}
}
public static void main(String[] args) throws IOException {
if (args.length < 1) {
System.out.println("Usage: java ADTParser <file.adt>");
System.exit(1);
}
ADTParser parser = new ADTParser(args[0]);
parser.read();
parser.printProperties();
parser.write("output.adt");
}
}
6. JavaScript Class for .ADT File Handling
The following JavaScript class (for Node.js) can open a .ADT file, decode its frames, read and print the properties to console, and write a modified version.
const fs = require('fs');
class ADTParser {
constructor(filepath) {
this.filepath = filepath;
this.data = null;
}
read() {
this.data = fs.readFileSync(this.filepath);
}
printProperties() {
let offset = 0;
let frameCount = 0;
while (offset + 7 < this.data.length) {
const syncword = (this.data.readUInt16BE(offset) >> 4) & 0xFFF;
if (syncword !== 0xFFF) {
offset++;
continue;
}
frameCount++;
const byte1 = this.data[offset + 1];
const byte2 = this.data[offset + 2];
const byte3 = this.data[offset + 3];
const byte4 = this.data[offset + 4];
const byte5 = this.data[offset + 5];
const byte6 = this.data[offset + 6];
const id = (byte1 >> 3) & 0x01;
const layer = (byte1 >> 1) & 0x03;
const protectionAbsent = byte1 & 0x01;
const profile = (byte2 >> 6) & 0x03;
const samplingFreqIndex = (byte2 >> 2) & 0x0F;
const privateBit = (byte2 >> 1) & 0x01;
const channelConfig = ((byte2 & 0x01) << 2) | ((byte3 >> 6) & 0x03);
const originalCopy = (byte3 >> 5) & 0x01;
const home = (byte3 >> 4) & 0x01;
const copyrightIdBit = (byte3 >> 3) & 0x01;
const copyrightIdStart = (byte3 >> 2) & 0x01;
const frameLength = ((byte3 & 0x03) << 11) | (byte4 << 3) | ((byte5 >> 5) & 0x07);
const bufferFullness = ((byte5 & 0x1F) << 6) | ((byte6 >> 2) & 0x3F);
const numRawBlocks = byte6 & 0x03;
let crc = null;
const headerSize = protectionAbsent ? 7 : 9;
if (!protectionAbsent && offset + 9 <= this.data.length) {
crc = this.data.readUInt16BE(offset + 7);
}
console.log(`Frame ${frameCount}:`);
console.log(` Syncword: 0xFFF`);
console.log(` ID: ${id}`);
console.log(` Layer: ${layer}`);
console.log(` Protection Absent: ${protectionAbsent}`);
console.log(` Profile: ${profile}`);
console.log(` Sampling Frequency Index: ${samplingFreqIndex}`);
console.log(` Private Bit: ${privateBit}`);
console.log(` Channel Configuration: ${channelConfig}`);
console.log(` Original/Copy: ${originalCopy}`);
console.log(` Home: ${home}`);
console.log(` Copyright Identification Bit: ${copyrightIdBit}`);
console.log(` Copyright Identification Start: ${copyrightIdStart}`);
console.log(` Frame Length: ${frameLength}`);
console.log(` Buffer Fullness: ${bufferFullness}`);
console.log(` Number of Raw Data Blocks: ${numRawBlocks + 1}`);
if (crc !== null) {
console.log(` CRC: 0x${crc.toString(16).toUpperCase()}`);
}
console.log('');
if (frameLength < headerSize) break;
offset += frameLength;
}
}
write(outputPath) {
fs.writeFileSync(outputPath, this.data);
}
}
// Example usage
if (process.argv.length < 3) {
console.log("Usage: node adt_parser.js <file.adt>");
process.exit(1);
}
const parser = new ADTParser(process.argv[2]);
parser.read();
parser.printProperties();
parser.write("output.adt");
7. C++ Class for .ADT File Handling
The following C++ class can open a .ADT file, decode its frames, read and print the properties to console, and write a modified version.
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
class ADTParser {
private:
std::string filepath;
std::vector<uint8_t> data;
public:
ADTParser(const std::string& fp) : filepath(fp) {}
void read() {
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) {
std::cerr << "Failed to open file." << std::endl;
return;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
data.resize(size);
file.read(reinterpret_cast<char*>(data.data()), size);
}
void printProperties() {
size_t offset = 0;
int frameCount = 0;
while (offset + 7 < data.size()) {
uint16_t syncword = ((data[offset] << 8) | data[offset + 1]) >> 4;
if (syncword != 0xFFF) {
offset++;
continue;
}
frameCount++;
uint8_t byte1 = data[offset + 1];
uint8_t byte2 = data[offset + 2];
uint8_t byte3 = data[offset + 3];
uint8_t byte4 = data[offset + 4];
uint8_t byte5 = data[offset + 5];
uint8_t byte6 = data[offset + 6];
int id = (byte1 >> 3) & 0x01;
int layer = (byte1 >> 1) & 0x03;
int protectionAbsent = byte1 & 0x01;
int profile = (byte2 >> 6) & 0x03;
int samplingFreqIndex = (byte2 >> 2) & 0x0F;
int privateBit = (byte2 >> 1) & 0x01;
int channelConfig = ((byte2 & 0x01) << 2) | ((byte3 >> 6) & 0x03);
int originalCopy = (byte3 >> 5) & 0x01;
int home = (byte3 >> 4) & 0x01;
int copyrightIdBit = (byte3 >> 3) & 0x01;
int copyrightIdStart = (byte3 >> 2) & 0x01;
int frameLength = ((byte3 & 0x03) << 11) | (byte4 << 3) | ((byte5 >> 5) & 0x07);
int bufferFullness = ((byte5 & 0x1F) << 6) | ((byte6 >> 2) & 0x3F);
int numRawBlocks = byte6 & 0x03;
uint16_t crc = 0;
int headerSize = protectionAbsent ? 7 : 9;
if (!protectionAbsent && offset + 9 <= data.size()) {
crc = (data[offset + 7] << 8) | data[offset + 8];
}
std::cout << "Frame " << frameCount << ":" << std::endl;
std::cout << " Syncword: 0xFFF" << std::endl;
std::cout << " ID: " << id << std::endl;
std::cout << " Layer: " << layer << std::endl;
std::cout << " Protection Absent: " << protectionAbsent << std::endl;
std::cout << " Profile: " << profile << std::endl;
std::cout << " Sampling Frequency Index: " << samplingFreqIndex << std::endl;
std::cout << " Private Bit: " << privateBit << std::endl;
std::cout << " Channel Configuration: " << channelConfig << std::endl;
std::cout << " Original/Copy: " << originalCopy << std::endl;
std::cout << " Home: " << home << std::endl;
std::cout << " Copyright Identification Bit: " << copyrightIdBit << std::endl;
std::cout << " Copyright Identification Start: " << copyrightIdStart << std::endl;
std::cout << " Frame Length: " << frameLength << std::endl;
std::cout << " Buffer Fullness: " << bufferFullness << std::endl;
std::cout << " Number of Raw Data Blocks: " << (numRawBlocks + 1) << std::endl;
if (!protectionAbsent) {
std::cout << " CRC: 0x" << std::hex << crc << std::dec << std::endl;
}
std::cout << std::endl;
if (frameLength < headerSize) break;
offset += frameLength;
}
}
void write(const std::string& outputPath) {
std::ofstream file(outputPath, std::ios::binary);
if (!file) {
std::cerr << "Failed to write file." << std::endl;
return;
}
file.write(reinterpret_cast<const char*>(data.data()), data.size());
}
};
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <file.adt>" << std::endl;
return 1;
}
ADTParser parser(argv[1]);
parser.read();
parser.printProperties();
parser.write("output.adt");
return 0;
}