Task 611: .REX File Format
Task 611: .REX File Format
1. List of all the properties of this file format intrinsic to its file system
The .REX file format is a proprietary audio loop format developed by Propellerhead (now Reason Studios) for their ReCycle software. It is used for sliced audio loops that can be tempo-adjusted without affecting pitch. The format is binary and not fully documented publicly, as it relies on the proprietary REX Shared Library for parsing, decryption, and decompression. Based on reverse engineering insights and general audio file structure conventions for such formats, the intrinsic properties (fields in the binary structure) are:
- Magic number (header identifier, typically a 4-byte string like 'REX!')
- Version (uint16, indicating the format version)
- Sample rate (uint32, in Hz, e.g., 44100)
- Channels (byte, 1 for mono in .rex, 2 for stereo in .rx2 variants)
- Bit depth (byte, e.g., 16 or 24)
- Number of slices (uint32, number of audio slices)
- Original tempo (float32, in BPM)
- Length in beats (uint32, the loop's length in musical beats)
- Slice points (array of uint32, positions in samples where slices start)
- Creator info (variable string, optional metadata about the creator or software)
- Audio data (variable, compressed audio chunks for each slice, typically in a proprietary compression like ADPCM)
Note: The exact byte offsets and compression details are proprietary, so the above is a high-level generalization. .rx2 is an updated version for stereo, but the structure is similar.
2. Two direct download links for files of format .REX
Here are two direct download links for sample .rx2 files (the stereo variant of .rex, commonly referred to as REX format):
- https://rhythm-lab.com/download/Harlem_Underground_Band_feat_Willis_Jackson_Aint_No_Sunshine_part2.rx2
- https://rhythm-lab.com/download/Oneness_of_Juju_Asanta_Sana_Okyerama_Asante_feat_Plunky.rx2
These are from free educational breakbeat packs on Rhythm Lab. Note: .rx2 is the modern extension for REX2 files, but the format is compatible with .rex.
3. Ghost blog embedded HTML JavaScript for drag and drop .REX file dump
Here's an embedded HTML page with JavaScript that allows drag-and-drop of a .REX file. It parses the binary file using the assumed structure and dumps the properties to the screen. (Note: This is a high-level implementation assuming the structure above; in practice, full parsing requires the proprietary library for audio data.)
4. Python class for .REX file
Here's a Python class that opens, decodes, reads, writes, and prints the properties. It assumes the structure above and uses struct for binary parsing. Audio data is not handled for simplicity.
import struct
class REXFile:
def __init__(self, filename):
self.filename = filename
self.magic = ''
self.version = 0
self.sample_rate = 0
self.channels = 0
self.bit_depth = 0
self.num_slices = 0
self.tempo = 0.0
self.length_beats = 0
self.slice_points = []
self.read()
def read(self):
with open(self.filename, 'rb') as f:
data = f.read()
self.magic = struct.unpack_from('<4s', data, 0)[0].decode('ascii')
self.version = struct.unpack_from('<H', data, 4)[0]
self.sample_rate = struct.unpack_from('<I', data, 6)[0]
self.channels = struct.unpack_from('<B', data, 10)[0]
self.bit_depth = struct.unpack_from('<B', data, 11)[0]
self.num_slices = struct.unpack_from('<I', data, 12)[0]
self.tempo = struct.unpack_from('<f', data, 16)[0]
self.length_beats = struct.unpack_from('<I', data, 20)[0]
self.slice_points = [struct.unpack_from('<I', data, 24 + i*4)[0] for i in range(self.num_slices)]
def print_properties(self):
print(f"Magic: {self.magic}")
print(f"Version: {self.version}")
print(f"Sample Rate: {self.sample_rate} Hz")
print(f"Channels: {self.channels}")
print(f"Bit Depth: {self.bit_depth}")
print(f"Number of Slices: {self.num_slices}")
print(f"Original Tempo: {self.tempo} BPM")
print(f"Length in Beats: {self.length_beats}")
print(f"Slice Points: {self.slice_points}")
def write(self, new_filename=None):
if not new_filename:
new_filename = self.filename
with open(new_filename, 'wb') as f:
f.write(struct.pack('<4s', self.magic.encode('ascii')))
f.write(struct.pack('<H', self.version))
f.write(struct.pack('<I', self.sample_rate))
f.write(struct.pack('<B', self.channels))
f.write(struct.pack('<B', self.bit_depth))
f.write(struct.pack('<I', self.num_slices))
f.write(struct.pack('<f', self.tempo))
f.write(struct.pack('<I', self.length_beats))
for point in self.slice_points:
f.write(struct.pack('<I', point))
# Audio data would be appended here if handled
# Example usage
# rex = REXFile('sample.rex')
# rex.print_properties()
# rex.tempo = 140.0
# rex.write('modified.rex')
5. Java class for .REX file
Here's a Java class using ByteBuffer for parsing.
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
public class REXFile {
private String filename;
private String magic;
private int version;
private int sampleRate;
private byte channels;
private byte bitDepth;
private int numSlices;
private float tempo;
private int lengthBeats;
private List<Integer> slicePoints = new ArrayList<>();
public REXFile(String filename) throws Exception {
this.filename = filename;
read();
}
private void read() throws Exception {
byte[] data = Files.readAllBytes(Paths.get(filename));
ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
byte[] magicBytes = new byte[4];
buffer.get(magicBytes);
magic = new String(magicBytes, "ASCII");
version = buffer.getShort();
sampleRate = buffer.getInt();
channels = buffer.get();
bitDepth = buffer.get();
numSlices = buffer.getInt();
tempo = buffer.getFloat();
lengthBeats = buffer.getInt();
for (int i = 0; i < numSlices; i++) {
slicePoints.add(buffer.getInt());
}
}
public void printProperties() {
System.out.println("Magic: " + magic);
System.out.println("Version: " + version);
System.out.println("Sample Rate: " + sampleRate + " Hz");
System.out.println("Channels: " + channels);
System.out.println("Bit Depth: " + bitDepth);
System.out.println("Number of Slices: " + numSlices);
System.out.println("Original Tempo: " + tempo + " BPM");
System.out.println("Length in Beats: " + lengthBeats);
System.out.println("Slice Points: " + slicePoints);
}
public void write(String newFilename) throws Exception {
if (newFilename == null) {
newFilename = filename;
}
ByteBuffer buffer = ByteBuffer.allocate(24 + numSlices * 4).order(ByteOrder.LITTLE_ENDIAN);
buffer.put(magic.getBytes("ASCII"));
buffer.putShort((short) version);
buffer.putInt(sampleRate);
buffer.put(channels);
buffer.put(bitDepth);
buffer.putInt(numSlices);
buffer.putFloat(tempo);
buffer.putInt(lengthBeats);
for (int point : slicePoints) {
buffer.putInt(point);
}
try (FileChannel channel = new RandomAccessFile(newFilename, "rw").getChannel()) {
buffer.flip();
channel.write(buffer);
}
}
// Example usage
// public static void main(String[] args) throws Exception {
// REXFile rex = new REXFile("sample.rex");
// rex.printProperties();
// rex.tempo = 140.0f;
// rex.write("modified.rex");
// }
}
6. JavaScript class for .REX file
Here's a JavaScript class for Node.js (using fs for file I/O).
const fs = require('fs');
class REXFile {
constructor(filename) {
this.filename = filename;
this.magic = '';
this.version = 0;
this.sampleRate = 0;
this.channels = 0;
this.bitDepth = 0;
this.numSlices = 0;
this.tempo = 0;
this.lengthBeats = 0;
this.slicePoints = [];
this.read();
}
read() {
const data = fs.readFileSync(this.filename);
const view = new DataView(data.buffer);
this.magic = String.fromCharCode(view.getUint8(0), view.getUint8(1), view.getUint8(2), view.getUint8(3));
this.version = view.getUint16(4, true);
this.sampleRate = view.getUint32(6, true);
this.channels = view.getUint8(10);
this.bitDepth = view.getUint8(11);
this.numSlices = view.getUint32(12, true);
this.tempo = view.getFloat32(16, true);
this.lengthBeats = view.getUint32(20, true);
for (let i = 0; i < this.numSlices; i++) {
this.slicePoints.push(view.getUint32(24 + i * 4, true));
}
}
printProperties() {
console.log(`Magic: ${this.magic}`);
console.log(`Version: ${this.version}`);
console.log(`Sample Rate: ${this.sampleRate} Hz`);
console.log(`Channels: ${this.channels}`);
console.log(`Bit Depth: ${this.bitDepth}`);
console.log(`Number of Slices: ${this.numSlices}`);
console.log(`Original Tempo: ${this.tempo} BPM`);
console.log(`Length in Beats: ${this.lengthBeats}`);
console.log(`Slice Points: ${this.slicePoints}`);
}
write(newFilename = this.filename) {
const buffer = new ArrayBuffer(24 + this.numSlices * 4);
const view = new DataView(buffer);
for (let i = 0; i < 4; i++) {
view.setUint8(i, this.magic.charCodeAt(i));
}
view.setUint16(4, this.version, true);
view.setUint32(6, this.sampleRate, true);
view.setUint8(10, this.channels);
view.setUint8(11, this.bitDepth);
view.setUint32(12, this.numSlices, true);
view.setFloat32(16, this.tempo, true);
view.setUint32(20, this.lengthBeats, true);
for (let i = 0; i < this.numSlices; i++) {
view.setUint32(24 + i * 4, this.slicePoints[i], true);
}
fs.writeFileSync(newFilename, new Uint8Array(buffer));
}
}
// Example usage
// const rex = new REXFile('sample.rex');
// rex.printProperties();
// rex.tempo = 140;
// rex.write('modified.rex');
7. C class for .REX file
In C, we use structs for data. Here's a C program with a struct-based "class" (using functions).
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct {
char magic[5];
uint16_t version;
uint32_t sample_rate;
uint8_t channels;
uint8_t bit_depth;
uint32_t num_slices;
float tempo;
uint32_t length_beats;
uint32_t *slice_points;
} REXFile;
REXFile* rex_open(const char* filename) {
FILE* f = fopen(filename, "rb");
if (!f) return NULL;
REXFile* rex = malloc(sizeof(REXFile));
fread(rex->magic, 1, 4, f);
rex->magic[4] = '\0';
fread(&rex->version, sizeof(uint16_t), 1, f);
fread(&rex->sample_rate, sizeof(uint32_t), 1, f);
fread(&rex->channels, sizeof(uint8_t), 1, f);
fread(&rex->bit_depth, sizeof(uint8_t), 1, f);
fread(&rex->num_slices, sizeof(uint32_t), 1, f);
fread(&rex->tempo, sizeof(float), 1, f);
fread(&rex->length_beats, sizeof(uint32_t), 1, f);
rex->slice_points = malloc(rex->num_slices * sizeof(uint32_t));
fread(rex->slice_points, sizeof(uint32_t), rex->num_slices, f);
fclose(f);
return rex;
}
void rex_print(REXFile* rex) {
printf("Magic: %s\n", rex->magic);
printf("Version: %u\n", rex->version);
printf("Sample Rate: %u Hz\n", rex->sample_rate);
printf("Channels: %u\n", rex->channels);
printf("Bit Depth: %u\n", rex->bit_depth);
printf("Number of Slices: %u\n", rex->num_slices);
printf("Original Tempo: %.2f BPM\n", rex->tempo);
printf("Length in Beats: %u\n", rex->length_beats);
printf("Slice Points: ");
for (uint32_t i = 0; i < rex->num_slices; i++) {
printf("%u ", rex->slice_points[i]);
}
printf("\n");
}
void rex_write(REXFile* rex, const char* new_filename) {
FILE* f = fopen(new_filename, "wb");
if (!f) return;
fwrite(rex->magic, 1, 4, f);
fwrite(&rex->version, sizeof(uint16_t), 1, f);
fwrite(&rex->sample_rate, sizeof(uint32_t), 1, f);
fwrite(&rex->channels, sizeof(uint8_t), 1, f);
fwrite(&rex->bit_depth, sizeof(uint8_t), 1, f);
fwrite(&rex->num_slices, sizeof(uint32_t), 1, f);
fwrite(&rex->tempo, sizeof(float), 1, f);
fwrite(&rex->length_beats, sizeof(uint32_t), 1, f);
fwrite(rex->slice_points, sizeof(uint32_t), rex->num_slices, f);
fclose(f);
}
void rex_close(REXFile* rex) {
free(rex->slice_points);
free(rex);
}
// Example usage
// int main() {
// REXFile* rex = rex_open("sample.rex");
// if (rex) {
// rex_print(rex);
// rex->tempo = 140.0f;
// rex_write(rex, "modified.rex");
// rex_close(rex);
// }
// return 0;
// }