Task 200: .EXS File Format

Task 200: .EXS File Format

.EXS File Format Specifications

The .EXS file format is a proprietary binary format used for instrument files in Apple's EXS24 sampler, which is part of Logic Pro. Apple does not provide an official public specification for the byte-level structure of .EXS files. Available information is based on high-level descriptions from third-party sources and reverse engineering efforts by developers of conversion tools (e.g., Chicken Systems Translator and ConvertWithMoss). The format is binary, not text-based, and contains structured data for sampler instruments. It is not documented in detail, which limits the ability to implement precise parsing, reading, and writing without proprietary knowledge or reverse engineering, which may have legal implications depending on usage.

1. List of Properties Intrinsic to the .EXS File Format

Based on available high-level documentation, the key properties of the .EXS file format include the following. These are intrinsic to the format's structure for defining sampler instruments, including references to external audio samples (not embedded in the .EXS file itself unless in a monolithic variant):

  • Instrument Name: The name of the sampler instrument.
  • Zones: Collections of sample mappings. Each zone includes:
  • Sample file reference (absolute path to external audio file in formats like WAVE, AIFF, SDII, or CAF).
  • Byte offset for sample data start in the referenced file.
  • Key range (low and high MIDI note numbers).
  • Velocity range (low and high velocity values).
  • Tuning (pitch adjustment in cents).
  • Volume adjustment.
  • Pan position.
  • Loop settings (loop mode, start point, end point).
  • Envelope parameters (attack, decay, sustain, release for amplitude).
  • Filter settings (cutoff, resonance).
  • Modulation sources (e.g., LFO, envelope).
  • Groups: Organizational units for zones, allowing shared parameters. Each group includes:
  • Group name.
  • Polyphony limit.
  • Keyswitching parameters.
  • Controller switching parameters.
  • Release trigger settings.
  • Amplitude ADSR envelope.
  • Filter cutoff and resonance adjustments.
  • Global Parameters: Instrument-wide settings, including:
  • Multiple envelopes and LFOs as modulators.
  • Overall volume, tuning, and filter controls.
  • Support for round-robin, velocity splits, and cross-switching (no true crossfading).
  • Sample Details: Metadata about referenced samples, including:
  • File format (e.g., 16-bit or 24-bit integer, no 32-bit float support).
  • Sample rate and bit depth.
  • Length and start offset.
  • File Header and Metadata: Binary header (e.g., some variants start with bytes like 01 01 00 or "SXE"), version information, and checksums (potential presence noted in sources, but not confirmed).
  • Limitations: Absolute paths only; no relative paths. No bank concept (each .EXS is a single instrument). Potential for out-of-sync issues if sample files change.

These properties are derived from functional descriptions rather than a complete byte-level schema. For a full implementation, reverse-engineered source code from tools like ConvertWithMoss would be required.

The following are direct download links to archives containing .EXS files (typically bundled with sample audio files in ZIP format for practicality, as .EXS files reference external samples):

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .EXS File Dump

The following is an embedded HTML page with JavaScript that allows drag-and-drop of a .EXS file. Due to the proprietary nature of the format and lack of a public byte-level specification, the script reads the file as binary data but cannot accurately parse and dump all properties without reverse-engineered logic. Instead, it demonstrates a basic structure by reading the file as an ArrayBuffer and attempting a simple hex dump, with placeholders for property extraction. In a production scenario, this would require integration with a reverse-engineered parser (e.g., adapted from Java code in ConvertWithMoss).

.EXS File Property Dumper
Drag and drop a .EXS file here

4. Python Class for .EXS File Handling

The following Python class opens a .EXS file, but due to the lack of a public byte-level specification, it cannot implement full decoding, reading, or writing. It provides a structure with placeholders for parsing logic (e.g., using struct module for binary unpacking). In practice, this would require reverse-engineered code.

import struct
import os

class ExsFileHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}

    def open_and_decode(self):
        if not os.path.exists(self.filepath):
            print("File not found.")
            return

        with open(self.filepath, 'rb') as f:
            data = f.read()
            # Placeholder for decoding - e.g., check header
            try:
                # Hypothetical header unpack (based on snippets: e.g., starts with b'\x01\x01\x00' or b'SXE')
                header = struct.unpack('>3B', data[:3])  # Example big-endian 3 bytes
                print(f"Header bytes: {header}")
                # TODO: Implement reverse-engineered parsing for zones, groups, etc.
                self.properties = {
                    'instrument_name': 'Parsed value (placeholder)',
                    'zones': [],
                    'groups': [],
                    # Add other properties
                }
            except struct.error:
                print("Error decoding binary data.")

    def read_properties(self):
        # Placeholder for reading decoded properties
        return self.properties

    def write_properties(self, new_properties):
        # Placeholder for writing - would require encoding logic
        print("Writing not implemented due to lack of specification.")

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

# Example usage
handler = ExsFileHandler('example.exs')
handler.open_and_decode()
handler.print_properties()

5. Java Class for .EXS File Handling

The following Java class provides a similar structure. Note that ConvertWithMoss (a Java tool) contains reverse-engineered parsing code, but without access to it, this is a placeholder implementation.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;

public class ExsFileHandler {
    private String filepath;
    private Map<String, Object> properties = new HashMap<>();

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

    public void openAndDecode() {
        File file = new File(filepath);
        if (!file.exists()) {
            System.out.println("File not found.");
            return;
        }

        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] data = new byte[(int) file.length()];
            fis.read(data);
            ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);  // Assume big-endian
            // Hypothetical header read
            byte[] header = new byte[3];
            buffer.get(header);
            System.out.printf("Header bytes: %02X %02X %02X%n", header[0], header[1], header[2]);
            // TODO: Reverse-engineered parsing
            properties.put("instrument_name", "Parsed value (placeholder)");
            properties.put("zones", new Object[0]);
            properties.put("groups", new Object[0]);
            // Add other properties
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        }
    }

    public Map<String, Object> readProperties() {
        return properties;
    }

    public void writeProperties(Map<String, Object> newProperties) {
        // Placeholder for writing
        System.out.println("Writing not implemented due to lack of specification.");
    }

    public void printProperties() {
        if (properties.isEmpty()) {
            System.out.println("No properties decoded.");
            return;
        }
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }

    public static void main(String[] args) {
        ExsFileHandler handler = new ExsFileHandler("example.exs");
        handler.openAndDecode();
        handler.printProperties();
    }
}

6. JavaScript Class for .EXS File Handling

The following JavaScript class is for Node.js (using fs module). It provides placeholders for parsing.

const fs = require('fs');

class ExsFileHandler {
    constructor(filepath) {
        this.filepath = filepath;
        this.properties = {};
    }

    openAndDecode() {
        if (!fs.existsSync(this.filepath)) {
            console.log('File not found.');
            return;
        }

        const data = fs.readFileSync(this.filepath);
        // Hypothetical parsing using Buffer
        const header = data.slice(0, 3).toString('hex');
        console.log(`Header bytes: ${header}`);
        // TODO: Reverse-engineered parsing
        this.properties = {
            instrument_name: 'Parsed value (placeholder)',
            zones: [],
            groups: [],
            // Add other properties
        };
    }

    readProperties() {
        return this.properties;
    }

    writeProperties(newProperties) {
        // Placeholder for writing
        console.log('Writing not implemented due to lack of specification.');
    }

    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}: ${JSON.stringify(value)}`);
        }
    }
}

// Example usage
const handler = new ExsFileHandler('example.exs');
handler.openAndDecode();
handler.printProperties();

7. C++ Class for .EXS File Handling

The following C++ class provides a similar placeholder implementation.

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <string>

class ExsFileHandler {
private:
    std::string filepath;
    std::map<std::string, std::string> properties;

public:
    ExsFileHandler(const std::string& filepath) : filepath(filepath) {}

    void openAndDecode() {
        std::ifstream file(filepath, std::ios::binary);
        if (!file.is_open()) {
            std::cout << "File not found." << std::endl;
            return;
        }

        std::vector<char> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        file.close();

        // Hypothetical header read (first 3 bytes)
        if (data.size() >= 3) {
            printf("Header bytes: %02X %02X %02X\n", static_cast<unsigned char>(data[0]), 
                   static_cast<unsigned char>(data[1]), static_cast<unsigned char>(data[2]));
        }
        // TODO: Reverse-engineered parsing
        properties["instrument_name"] = "Parsed value (placeholder)";
        properties["zones"] = "[]";
        properties["groups"] = "[]";
        // Add other properties
    }

    std::map<std::string, std::string> readProperties() {
        return properties;
    }

    void writeProperties(const std::map<std::string, std::string>& newProperties) {
        // Placeholder for writing
        std::cout << "Writing not implemented due to lack of specification." << std::endl;
    }

    void printProperties() {
        if (properties.empty()) {
            std::cout << "No properties decoded." << std::endl;
            return;
        }
        for (const auto& pair : properties) {
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
    }
};

int main() {
    ExsFileHandler handler("example.exs");
    handler.openAndDecode();
    handler.printProperties();
    return 0;
}