Task 046: .AU File Type
Task 046: .AU File Type
1. Properties of the .AU File Format
The .AU file format, developed by Sun Microsystems, is a simple audio file format commonly used on UNIX-based systems like NeXT and early web pages. Newer .AU files include a header, an optional annotation block, and audio data, all stored in big-endian byte order. The following properties are intrinsic to the .AU file format's structure, as derived from its specifications:
- Magic Number: A 4-byte identifier at the start of the file, always set to
0x2e736e64
(ASCII ".snd"). - Data Offset: A 4-byte unsigned 32-bit integer indicating the offset (in bytes) to the start of the audio data. Minimum value is 28 (24-byte header + minimum 4-byte annotation).
- Data Size: A 4-byte unsigned 32-bit integer specifying the size of the audio data in bytes. Set to
0xffffffff
if unknown. - Encoding: A 4-byte unsigned 32-bit integer defining the audio data encoding format. Possible values include:
- 1: 8-bit G.711 μ-law
- 2: 8-bit linear PCM
- 3: 16-bit linear PCM
- 4: 24-bit linear PCM
- 5: 32-bit linear PCM
- 6: 32-bit IEEE floating point
- 7: 64-bit IEEE floating point
- 23: 4-bit CCITT G.721 ADPCM
- 24: CCITT G.722 ADPCM
- Sample Rate: A 4-byte unsigned 32-bit integer indicating the number of samples per second (e.g., 8000 Hz, 44100 Hz).
- Channels: A 4-byte unsigned 32-bit integer specifying the number of interleaved audio channels (e.g., 1 for mono, 2 for stereo).
- Annotation Block: A variable-length field following the header, containing optional metadata (e.g., text annotations). Its size is implied by the data offset minus the 24-byte header length.
Note: This response focuses on the Sun Microsystems .AU format, not the proprietary Audacity .AU format, which is unrelated and uses a different structure.
2. Python Class for .AU File Handling
import struct
import os
class AUFile:
def __init__(self, filename):
self.filename = filename
self.magic = None
self.data_offset = None
self.data_size = None
self.encoding = None
self.sample_rate = None
self.channels = None
self.annotation = None
def read_au(self):
"""Read and decode .AU file properties."""
try:
with open(self.filename, 'rb') as f:
# Read 24-byte header (6 unsigned 32-bit integers in big-endian)
header = f.read(24)
if len(header) < 24:
raise ValueError("File too short for AU header")
# Unpack header (big-endian, > means big-endian, 6I = 6 unsigned integers)
self.magic, self.data_offset, self.data_size, self.encoding, \
self.sample_rate, self.channels = struct.unpack('>6I', header)
# Verify magic number
if self.magic != 0x2e736e64:
raise ValueError("Invalid AU file: incorrect magic number")
# Read annotation block
annotation_size = self.data_offset - 24
if annotation_size > 0:
self.annotation = f.read(annotation_size).decode('utf-8', errors='ignore')
else:
self.annotation = ""
except Exception as e:
print(f"Error reading AU file: {e}")
def write_au(self, output_filename, audio_data):
"""Write a new .AU file with the current properties and provided audio data."""
try:
with open(output_filename, 'wb') as f:
# Prepare header
annotation_bytes = self.annotation.encode('utf-8') if self.annotation else b''
self.data_offset = 24 + len(annotation_bytes)
self.data_size = len(audio_data)
# Pack header
header = struct.pack('>6I', self.magic or 0x2e736e64, self.data_offset,
self.data_size, self.encoding or 1,
self.sample_rate or 8000, self.channels or 1)
f.write(header)
# Write annotation
f.write(annotation_bytes)
# Write audio data
f.write(audio_data)
except Exception as e:
print(f"Error writing AU file: {e}")
def print_properties(self):
"""Print all .AU file properties to console."""
encoding_map = {
1: "8-bit G.711 μ-law", 2: "8-bit linear PCM", 3: "16-bit linear PCM",
4: "24-bit linear PCM", 5: "32-bit linear PCM", 6: "32-bit IEEE floating point",
7: "64-bit IEEE floating point", 23: "4-bit CCITT G.721 ADPCM", 24: "CCITT G.722 ADPCM"
}
print(f"AU File Properties ({self.filename}):")
print(f"Magic Number: 0x{self.magic:08x} ({'.snd' if self.magic == 0x2e736e64 else 'Invalid'})")
print(f"Data Offset: {self.data_offset} bytes")
print(f"Data Size: {self.data_size} bytes")
print(f"Encoding: {encoding_map.get(self.encoding, 'Unknown')} ({self.encoding})")
print(f"Sample Rate: {self.sample_rate} Hz")
print(f"Channels: {self.channels} ({'Mono' if self.channels == 1 else 'Stereo' if self.channels == 2 else 'Multi-channel'})")
print(f"Annotation: {self.annotation or 'None'}")
# Example usage
if __name__ == "__main__":
au = AUFile("sample.au")
au.read_au()
au.print_properties()
# Example: Write a new AU file (using dummy audio data)
dummy_audio = b'\x00' * 1000 # Placeholder audio data
au.write_au("output.au", dummy_audio)
This Python class uses struct
to handle big-endian binary data, reads the header and annotation, and provides methods to write a new .AU file. It assumes the audio data is provided externally for writing.
3. Java Class for .AU File Handling
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class AUFile {
private String filename;
private long magic;
private long dataOffset;
private long dataSize;
private long encoding;
private long sampleRate;
private long channels;
private String annotation;
public AUFile(String filename) {
this.filename = filename;
}
public void readAU() throws IOException {
try (DataInputStream dis = new DataInputStream(new FileInputStream(filename))) {
// Read 24-byte header
byte[] header = new byte[24];
if (dis.read(header) != 24) {
throw new IOException("File too short for AU header");
}
ByteBuffer bb = ByteBuffer.wrap(header).order(ByteOrder.BIG_ENDIAN);
magic = Integer.toUnsignedLong(bb.getInt());
dataOffset = Integer.toUnsignedLong(bb.getInt());
dataSize = Integer.toUnsignedLong(bb.getInt());
encoding = Integer.toUnsignedLong(bb.getInt());
sampleRate = Integer.toUnsignedLong(bb.getInt());
channels = Integer.toUnsignedLong(bb.getInt());
if (magic != 0x2e736e64L) {
throw new IOException("Invalid AU file: incorrect magic number");
}
// Read annotation
int annotationSize = (int) (dataOffset - 24);
if (annotationSize > 0) {
byte[] annotationBytes = new byte[annotationSize];
dis.read(annotationBytes);
annotation = new String(annotationBytes, "UTF-8");
} else {
annotation = "";
}
}
}
public void writeAU(String outputFilename, byte[] audioData) throws IOException {
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(outputFilename))) {
byte[] annotationBytes = annotation != null ? annotation.getBytes("UTF-8") : new byte[0];
dataOffset = 24 + annotationBytes.length;
dataSize = audioData.length;
ByteBuffer bb = ByteBuffer.allocate(24).order(ByteOrder.BIG_ENDIAN);
bb.putInt((int) (magic != 0 ? magic : 0x2e736e64L));
bb.putInt((int) dataOffset);
bb.putInt((int) dataSize);
bb.putInt((int) (encoding != 0 ? encoding : 1));
bb.putInt((int) (sampleRate != 0 ? sampleRate : 8000));
bb.putInt((int) (channels != 0 ? channels : 1));
dos.write(bb.array());
dos.write(annotationBytes);
dos.write(audioData);
}
}
public void printProperties() {
String[] encodingMap = {
"", "8-bit G.711 μ-law", "8-bit linear PCM", "16-bit linear PCM",
"24-bit linear PCM", "32-bit linear PCM", "32-bit IEEE floating point",
"64-bit IEEE floating point"
};
String encodingStr = encoding < encodingMap.length ? encodingMap[(int) encoding] : "Unknown";
System.out.println("AU File Properties (" + filename + "):");
System.out.println("Magic Number: 0x" + Long.toHexString(magic) + (magic == 0x2e736e64L ? " (.snd)" : " (Invalid)"));
System.out.println("Data Offset: " + dataOffset + " bytes");
System.out.println("Data Size: " + dataSize + " bytes");
System.out.println("Encoding: " + encodingStr + " (" + encoding + ")");
System.out.println("Sample Rate: " + sampleRate + " Hz");
System.out.println("Channels: " + channels + " (" + (channels == 1 ? "Mono" : channels == 2 ? "Stereo" : "Multi-channel") + ")");
System.out.println("Annotation: " + (annotation.isEmpty() ? "None" : annotation));
}
public static void main(String[] args) {
try {
AUFile au = new AUFile("sample.au");
au.readAU();
au.printProperties();
byte[] dummyAudio = new byte[1000]; // Placeholder
au.writeAU("output.au", dummyAudio);
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
}
This Java class uses DataInputStream
and ByteBuffer
for big-endian handling, with methods to read, write, and print .AU file properties.
4. JavaScript Class for .AU File Handling
const fs = require('fs').promises;
class AUFile {
constructor(filename) {
this.filename = filename;
this.magic = null;
this.dataOffset = null;
this.dataSize = null;
this.encoding = null;
this.sampleRate = null;
this.channels = null;
this.annotation = null;
}
async readAU() {
try {
const buffer = await fs.readFile(this.filename);
if (buffer.length < 24) throw new Error("File too short for AU header");
const view = new DataView(buffer.buffer);
this.magic = view.getUint32(0, false); // false for big-endian
this.dataOffset = view.getUint32(4, false);
this.dataSize = view.getUint32(8, false);
this.encoding = view.getUint32(12, false);
this.sampleRate = view.getUint32(16, false);
this.channels = view.getUint32(20, false);
if (this.magic !== 0x2e736e64) {
throw new Error("Invalid AU file: incorrect magic number");
}
const annotationSize = this.dataOffset - 24;
if (annotationSize > 0) {
this.annotation = buffer.slice(24, this.dataOffset).toString('utf-8');
} else {
this.annotation = "";
}
} catch (e) {
console.error(`Error reading AU file: ${e.message}`);
}
}
async writeAU(outputFilename, audioData) {
try {
const annotationBytes = this.annotation ? Buffer.from(this.annotation, 'utf-8') : Buffer.alloc(0);
this.dataOffset = 24 + annotationBytes.length;
this.dataSize = audioData.length;
const buffer = Buffer.alloc(24 + annotationBytes.length + audioData.length);
const view = new DataView(buffer.buffer);
view.setUint32(0, this.magic || 0x2e736e64, false);
view.setUint32(4, this.dataOffset, false);
view.setUint32(8, this.dataSize, false);
view.setUint32(12, this.encoding || 1, false);
view.setUint32(16, this.sampleRate || 8000, false);
view.setUint32(20, this.channels || 1, false);
buffer.set(annotationBytes, 24);
buffer.set(audioData, 24 + annotationBytes.length);
await fs.writeFile(outputFilename, buffer);
} catch (e) {
console.error(`Error writing AU file: ${e.message}`);
}
}
printProperties() {
const encodingMap = {
1: "8-bit G.711 μ-law", 2: "8-bit linear PCM", 3: "16-bit linear PCM",
4: "24-bit linear PCM", 5: "32-bit linear PCM", 6: "32-bit IEEE floating point",
7: "64-bit IEEE floating point", 23: "4-bit CCITT G.721 ADPCM", 24: "CCITT G.722 ADPCM"
};
console.log(`AU File Properties (${this.filename}):`);
console.log(`Magic Number: 0x${this.magic.toString(16)} (${this.magic === 0x2e736e64 ? '.snd' : 'Invalid'})`);
console.log(`Data Offset: ${this.dataOffset} bytes`);
console.log(`Data Size: ${this.dataSize} bytes`);
console.log(`Encoding: ${encodingMap[this.encoding] || 'Unknown'} (${this.encoding})`);
console.log(`Sample Rate: ${this.sampleRate} Hz`);
console.log(`Channels: ${this.channels} (${this.channels === 1 ? 'Mono' : this.channels === 2 ? 'Stereo' : 'Multi-channel'})`);
console.log(`Annotation: ${this.annotation || 'None'}`);
}
}
// Example usage
(async () => {
const au = new AUFile("sample.au");
await au.readAU();
au.printProperties();
const dummyAudio = Buffer.alloc(1000); // Placeholder
await au.writeAU("output.au", dummyAudio);
})();
This JavaScript class uses Node.js fs
module and DataView
for big-endian handling, with async methods for file operations.
5. C Class for .AU File Handling
Since C does not have classes, we use a struct
and functions to emulate class-like behavior.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define AU_MAGIC 0x2e736e64
typedef struct {
char* filename;
uint32_t magic;
uint32_t data_offset;
uint32_t data_size;
uint32_t encoding;
uint32_t sample_rate;
uint32_t channels;
char* annotation;
} AUFile;
AUFile* au_create(const char* filename) {
AUFile* au = (AUFile*)malloc(sizeof(AUFile));
au->filename = strdup(filename);
au->magic = 0;
au->data_offset = 0;
au->data_size = 0;
au->encoding = 0;
au->sample_rate = 0;
au->channels = 0;
au->annotation = NULL;
return au;
}
void au_destroy(AUFile* au) {
if (au->filename) free(au->filename);
if (au->annotation) free(au->annotation);
free(au);
}
int read_au(AUFile* au) {
FILE* file = fopen(au->filename, "rb");
if (!file) {
printf("Error opening file: %s\n", au->filename);
return -1;
}
uint8_t header[24];
if (fread(header, 1, 24, file) != 24) {
printf("File too short for AU header\n");
fclose(file);
return -1;
}
// Big-endian reads
au->magic = (header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3];
au->data_offset = (header[4] << 24) | (header[5] << 16) | (header[6] << 8) | header[7];
au->data_size = (header[8] << 24) | (header[9] << 16) | (header[10] << 8) | header[11];
au->encoding = (header[12] << 24) | (header[13] << 16) | (header[14] << 8) | header[15];
au->sample_rate = (header[16] << 24) | (header[17] << 16) | (header[18] << 8) | header[19];
au->channels = (header[20] << 24) | (header[21] << 16) | (header[22] << 8) | header[23];
if (au->magic != AU_MAGIC) {
printf("Invalid AU file: incorrect magic number\n");
fclose(file);
return -1;
}
uint32_t annotation_size = au->data_offset - 24;
if (annotation_size > 0) {
au->annotation = (char*)malloc(annotation_size + 1);
if (fread(au->annotation, 1, annotation_size, file) != annotation_size) {
printf("Error reading annotation\n");
fclose(file);
return -1;
}
au->annotation[annotation_size] = '\0';
} else {
au->annotation = strdup("");
}
fclose(file);
return 0;
}
int write_au(AUFile* au, const char* output_filename, uint8_t* audio_data, uint32_t audio_data_size) {
FILE* file = fopen(output_filename, "wb");
if (!file) {
printf("Error opening output file: %s\n", output_filename);
return -1;
}
uint8_t header[24];
au->data_offset = 24 + (au->annotation ? strlen(au->annotation) : 0);
au->data_size = audio_data_size;
// Big-endian writes
header[0] = (au->magic ? au->magic : AU_MAGIC) >> 24; header[1] = (au->magic ? au->magic : AU_MAGIC) >> 16;
header[2] = (au->magic ? au->magic : AU_MAGIC) >> 8; header[3] = au->magic ? au->magic : AU_MAGIC;
header[4] = au->data_offset >> 24; header[5] = au->data_offset >> 16;
header[6] = au->data_offset >> 8; header[7] = au->data_offset;
header[8] = au->data_size >> 24; header[9] = au->data_size >> 16;
header[10] = au->data_size >> 8; header[11] = au->data_size;
header[12] = (au->encoding ? au->encoding : 1) >> 24; header[13] = (au->encoding ? au->encoding : 1) >> 16;
header[14] = (au->encoding ? au->encoding : 1) >> 8; header[15] = au->encoding ? au->encoding : 1;
header[16] = (au->sample_rate ? au->sample_rate : 8000) >> 24; header[17] = (au->sample_rate ? au->sample_rate : 8000) >> 16;
header[18] = (au->sample_rate ? au->sample_rate : 8000) >> 8; header[19] = au->sample_rate ? au->sample_rate : 8000;
header[20] = (au->channels ? au->channels : 1) >> 24; header[21] = (au->channels ? au->channels : 1) >> 16;
header[22] = (au->channels ? au->channels : 1) >> 8; header[23] = au->channels ? au->channels : 1;
fwrite(header, 1, 24, file);
if (au->annotation) fwrite(au->annotation, 1, strlen(au->annotation), file);
fwrite(audio_data, 1, audio_data_size, file);
fclose(file);
return 0;
}
void print_properties(AUFile* au) {
const char* encoding_map[] = {
"", "8-bit G.711 μ-law", "8-bit linear PCM", "16-bit linear PCM",
"24-bit linear PCM", "32-bit linear PCM", "32-bit IEEE floating point",
"64-bit IEEE floating point"
};
printf("AU File Properties (%s):\n", au->filename);
printf("Magic Number: 0x%08x (%s)\n", au->magic, au->magic == AU_MAGIC ? ".snd" : "Invalid");
printf("Data Offset: %u bytes\n", au->data_offset);
printf("Data Size: %u bytes\n", au->data_size);
printf("Encoding: %s (%u)\n", au->encoding < 8 ? encoding_map[au->encoding] : "Unknown", au->encoding);
printf("Sample Rate: %u Hz\n", au->sample_rate);
printf("Channels: %u (%s)\n", au->channels, au->channels == 1 ? "Mono" : au->channels == 2 ? "Stereo" : "Multi-channel");
printf("Annotation: %s\n", au->annotation ? au->annotation : "None");
}
int main() {
AUFile* au = au_create("sample.au");
if (read_au(au) == 0) {
print_properties(au);
uint8_t dummy_audio[1000] = {0}; // Placeholder
write_au(au, "output.au", dummy_audio, 1000);
}
au_destroy(au);
return 0;
}
This C implementation uses a struct
and functions to handle .AU files, with manual big-endian conversions due to C’s lack of built-in endian handling.
Notes
- File Access: All classes assume the file exists and is accessible. Error handling is included for common issues like invalid headers or file access failures.
- Writing Audio Data: The write methods require audio data as input. In practice, you would provide actual audio data in the correct format (e.g., μ-law or PCM).
- Big-Endian: All implementations respect the big-endian requirement of the .AU format.
- Testing: The example usage in each class assumes a file named "sample.au". Replace with an actual .AU file for testing. The write methods create an "output.au" file with placeholder audio data.
- Audacity .AU: The proprietary Audacity .AU format is not addressed here, as it differs significantly and is tied to .AUP project files.
These implementations should handle standard .AU files as per the Sun Microsystems specification. Let me know if you need further clarification or additional features!
- Properties:
- Magic number (4 bytes, '.snd')
- Data offset (4 bytes, uint32 big-endian)
- Data size (4 bytes, uint32 big-endian, ~0 if unknown)
- Encoding (4 bytes, uint32 big-endian)
- Sample rate (4 bytes, uint32 big-endian)
- Channels (4 bytes, uint32 big-endian)
- Annotation (variable bytes)
- Python class:
import struct
import os
class AuFile:
def __init__(self):
self.magic = b'.snd'
self.offset = 0
self.size = 0
self.encoding = 0
self.rate = 0
self.channels = 0
self.annotation = b''
def read(self, filename):
with open(filename, 'rb') as f:
header = f.read(24)
if len(header) < 24:
raise ValueError("Invalid AU file")
self.magic, self.offset, self.size, self.encoding, self.rate, self.channels = struct.unpack('>4s5I', header)
if self.magic != b'.snd':
raise ValueError("Not AU file")
ann_len = self.offset - 24
self.annotation = f.read(ann_len)
def write(self, filename, data=b''):
ann_len = len(self.annotation)
self.offset = 24 + ann_len
self.size = len(data) if data else 0xffffffff
with open(filename, 'wb') as f:
header = struct.pack('>4s5I', self.magic, self.offset, self.size, self.encoding, self.rate, self.channels)
f.write(header)
f.write(self.annotation)
if data:
f.write(data)
- Java class:
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Paths;
public class AuFile {
private byte[] magic = ".snd".getBytes();
private int offset;
private int size;
private int encoding;
private int rate;
private int channels;
private byte[] annotation = new byte[0];
public void read(String filename) throws IOException {
byte[] header = Files.readAllBytes(Paths.get(filename));
if (header.length < 24) throw new IOException("Invalid AU file");
ByteBuffer bb = ByteBuffer.wrap(header).order(ByteOrder.BIG_ENDIAN);
byte[] mag = new byte[4];
bb.get(mag);
if (!new String(mag).equals(".snd")) throw new IOException("Not AU file");
offset = bb.getInt();
size = bb.getInt();
encoding = bb.getInt();
rate = bb.getInt();
channels = bb.getInt();
int annLen = offset - 24;
annotation = new byte[annLen];
System.arraycopy(header, 24, annotation, 0, annLen);
}
public void write(String filename, byte[] data) throws IOException {
int annLen = annotation.length;
offset = 24 + annLen;
size = data != null ? data.length : -1;
ByteBuffer bb = ByteBuffer.allocate(24).order(ByteOrder.BIG_ENDIAN);
bb.put(magic);
bb.putInt(offset);
bb.putInt(size);
bb.putInt(encoding);
bb.putInt(rate);
bb.putInt(channels);
try (FileOutputStream fos = new FileOutputStream(filename)) {
fos.write(bb.array());
fos.write(annotation);
if (data != null) fos.write(data);
}
}
}
- JavaScript class:
const fs = require('fs');
class AuFile {
constructor() {
this.magic = Buffer.from('.snd');
this.offset = 0;
this.size = 0;
this.encoding = 0;
this.rate = 0;
this.channels = 0;
this.annotation = Buffer.alloc(0);
}
read(filename) {
const buffer = fs.readFileSync(filename);
if (buffer.length < 24) throw new Error('Invalid AU file');
this.magic = buffer.slice(0, 4);
if (this.magic.toString() !== '.snd') throw new Error('Not AU file');
this.offset = buffer.readUInt32BE(4);
this.size = buffer.readUInt32BE(8);
this.encoding = buffer.readUInt32BE(12);
this.rate = buffer.readUInt32BE(16);
this.channels = buffer.readUInt32BE(20);
const annLen = this.offset - 24;
this.annotation = buffer.slice(24, 24 + annLen);
}
write(filename, data = Buffer.alloc(0)) {
const annLen = this.annotation.length;
this.offset = 24 + annLen;
this.size = data.length || 0xffffffff;
const header = Buffer.alloc(24);
header.write('.snd', 0, 4);
header.writeUInt32BE(this.offset, 4);
header.writeUInt32BE(this.size, 8);
header.writeUInt32BE(this.encoding, 12);
header.writeUInt32BE(this.rate, 16);
header.writeUInt32BE(this.channels, 20);
fs.writeFileSync(filename, Buffer.concat([header, this.annotation, data]));
}
}
- C++ class:
#include <fstream>
#include <vector>
#include <stdexcept>
#include <cstring>
#include <cstdint>
#include <algorithm>
class AuFile {
public:
std::vector<uint8_t> magic = {'.', 's', 'n', 'd'};
uint32_t offset = 0;
uint32_t size = 0;
uint32_t encoding = 0;
uint32_t rate = 0;
uint32_t channels = 0;
std::vector<uint8_t> annotation;
void read(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file) throw std::runtime_error("Cannot open file");
std::vector<uint8_t> header(24);
file.read(reinterpret_cast<char*>(header.data()), 24);
if (file.gcount() < 24) throw std::runtime_error("Invalid AU file");
if (std::memcmp(header.data(), ".snd", 4) != 0) throw std::runtime_error("Not AU file");
offset = be_to_host(*reinterpret_cast<uint32_t*>(header.data() + 4));
size = be_to_host(*reinterpret_cast<uint32_t*>(header.data() + 8));
encoding = be_to_host(*reinterpret_cast<uint32_t*>(header.data() + 12));
rate = be_to_host(*reinterpret_cast<uint32_t*>(header.data() + 16));
channels = be_to_host(*reinterpret_cast<uint32_t*>(header.data() + 20));
uint32_t ann_len = offset - 24;
annotation.resize(ann_len);
file.read(reinterpret_cast<char*>(annotation.data()), ann_len);
}
void write(const std::string& filename, const std::vector<uint8_t>& data = {}) {
uint32_t ann_len = annotation.size();
offset = 24 + ann_len;
size = data.size() ? data.size() : 0xffffffff;
std::ofstream file(filename, std::ios::binary);
if (!file) throw std::runtime_error("Cannot write file");
file.write(".snd", 4);
write_be_uint32(file, offset);
write_be_uint32(file, size);
write_be_uint32(file, encoding);
write_be_uint32(file, rate);
write_be_uint32(file, channels);
file.write(reinterpret_cast<const char*>(annotation.data()), ann_len);
file.write(reinterpret_cast<const char*>(data.data()), data.size());
}
private:
uint32_t be_to_host(uint32_t val) {
return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val & 0xff0000) >> 8) | ((val >> 24) & 0xff);
}
void write_be_uint32(std::ofstream& file, uint32_t val) {
uint8_t bytes[4] = {
static_cast<uint8_t>((val >> 24) & 0xff),
static_cast<uint8_t>((val >> 16) & 0xff),
static_cast<uint8_t>((val >> 8) & 0xff),
static_cast<uint8_t>(val & 0xff)
};
file.write(reinterpret_cast<char*>(bytes), 4);
}
};