Watch
2
0
Fork
You've already forked jspiel
0
Java RIFF I/O
  • Java 99.2%
  • Smalltalk 0.6%
  • CSS 0.1%
  • Shell 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Mark Raynsford d2305b65f0
Some checks are pending
main-RHEL10-x86_64-jdk25 / Build (push) Waiting to run
main-RHEL10-x86_64-jdk26 / Build (push) Waiting to run
main-RHEL9-x86_64-jdk25 / Build (push) Waiting to run
main-RHEL9-x86_64-jdk26 / Build (push) Waiting to run
Update workflows.
2026-07-31 18:42:57 +01:00
.forgejo Update workflows. 2026-07-31 18:42:57 +01:00
com.io7m.jspiel.api Begin next development iteration. 2024-05-16 11:25:55 +00:00
com.io7m.jspiel.cmdline Update package version. 2024-05-16 11:36:46 +00:00
com.io7m.jspiel.tests Begin next development iteration. 2024-05-16 11:25:55 +00:00
com.io7m.jspiel.vanilla Begin next development iteration. 2024-05-16 11:25:55 +00:00
src/site/resources Update site metadata. 2025-08-09 15:07:04 +00:00
.gitignore Update .gitignore. 2024-05-07 16:01:45 +00:00
.gitmodules Move to new organization. 2024-05-01 18:31:55 +00:00
doc7m-books.json Add doc7m-books.json 2026-07-31 18:36:48 +01:00
pom.xml Migrate project. 2026-06-27 10:11:31 +01:00
README-CHANGES.xml Mark release 1.0.0 2024-05-16 11:25:44 +00:00
README-LICENSE.txt Update LICENSE 2023-08-13 21:07:25 +00:00
README.in Migrate project. 2026-06-27 10:11:31 +01:00
README.md Migrate project. 2026-06-27 10:11:31 +01:00

jspiel

Maven Central Maven Central (snapshot) Java Version

com.io7m.jspiel

jspiel

The jspiel package implements a set of types and functions for manipulating RIFF files. It provides efficient parsing and serialization of RIFF files.

Features

  • Ultra-efficient memory-mapped RIFF parsing.
  • Safe, easy, and correct lazy RIFF file output with chunk streaming.
  • Command-line tools and API.
  • Strongly-typed interfaces with a heavy emphasis on immutable value types.
  • Fully documented (JavaDoc).
  • Example code included.
  • OSGi-ready
  • JPMS-ready
  • ISC license.
  • High-coverage automated test suite.

Usage

Reading

Memory-map a RIFF file and use a RiffFileParserType to obtain the RIFF structure of the file:

final var parsers =
  ServiceLoader.load(RiffFileParserProviderType.class)
    .findFirst()
    .orElseThrow(() -> new IllegalStateException("No RIFF file parser service available"));

try (var channel = FileChannel.open(path, READ)) {
  final var map = channel.map(READ_ONLY, 0L, channel.size());
  final var parser = parsers.createForByteBuffer(this.path.toUri(), map);
  final var file = parser.parse();

  for (final var chunk : file.chunks()) {
    // Examine chunks and use the information to seek the channel
    // to different offsets within the chunks to obtain data.
    //
    // The chunks form a tree structure and have full parent/child
    // connectivity information.
  }
}

Writing

Use a RiffFileBuilderType to express the RIFF structure of the intended file, and then pass the built description to a RiffFileWriterType to serialize it.

final var builders =
  ServiceLoader.load(RiffFileBuilderProviderType.class)
    .findFirst()
    .orElseThrow(() -> new IllegalStateException("No RIFF file builder service available"));

final var writers =
  ServiceLoader.load(RiffFileWriterProviderType.class)
    .findFirst()
    .orElseThrow(() -> new IllegalStateException("No RIFF file writer service available"));

// Create a little-endian RIFF file description

final var builder =
  this.builders()
    .create(LITTLE_ENDIAN);

// The root chunk of the RIFF file must be RIFF, and has an "io7m" form
// in this case.

try (var root = builder.setRootChunk(RiffChunkID.of("RIFF"), "io7m")) {

  // The AAAA chunk is variable-length; the given writer function
  // can write as much data as it likes. The writer function is
  // presented with an NIO seekable byte channel interface where position 0
  // on the channel represents the start of the data section in the
  // chunk.

  try (var c = root.addSubChunk(RiffChunkID.of("AAAA"))) {
    c.setDataWriter(data -> data.write(...));
  }

  // The BBBB chunk is declared to have a length of 27 bytes; the given
  // writer function can write at most 27 bytes (and writing will be
  // terminated with an exception if it tries to write more). The library
  // will zero-pad any space that the writer does not use.
  //
  // The library also takes care of adding the padding
  // byte required by the RIFF specification (because 27 is not evenly
  // divisible into 16-bit words and so must be padded to 28 bytes).

  try (var c = root.addSubChunk(RiffChunkID.of("BBBB"))) {
    c.setSize(27L);
    c.setDataWriter(data -> data.write(...));
  }

  // The LIST chunk with form "cwss" contains three subchunks of varying
  // types and sizes.

  try (var c = root.addSubChunk(RiffChunkID.of("LIST"))) {
    c.setForm("cwss");

    try (var d = c.addSubChunk(RiffChunkID.of("cw05"))) {
      d.setSize(5L);
      d.setDataWriter(data -> data.write(...));
    }
    try (var d = c.addSubChunk(RiffChunkID.of("cw10"))) {
      d.setSize(10L);
      d.setDataWriter(data -> data.write(...));
    }
    try (var d = c.addSubChunk(RiffChunkID.of("cw20"))) {
      d.setSize(20L);
      d.setDataWriter(data -> data.write(...));
    }
  }
}

// Build the description and serialize it to the given byte channel.
// The writer functions specified in the description are called as the
// file is serialized.

final var description = builder.build();
try (final var channel = FileChannel.open(file, TRUNCATE_EXISTING, WRITE, CREATE)) {
  final var writer = writers.createForChannel(URI.create(file.toUri()), description, channel);
  writer.write();
}