io7m.com | software | jnoisetype
com.io7m.jnoisetype
Features
- Ultra-efficient memory-mapped SoundFontⓡ parsing
- Safe, easy, and correct lazy SoundFontⓡ file writing
- 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
- Hostile test suite that subjects parsers to randomly corrupted data to test robustness
Releases
The most recently published version of the software is 0.0.5.
Source code and binaries are available from the repository.
Documentation
Documentation for the 0.0.5 release is available for reading online.
Documentation for current and older releases is archived in the repository.
User documentation
Reading
Memory-map a SoundFontⓡ file and use a parser to extract data from the file, and an interpreter to build a useful model of the data:
new IllegalStateException( "No SoundFontⓡ file parser service available")); final var interpreters = ServiceLoader.load(NTInterpreterProviderType.class) .findFirst() .orElseThrow(() -> new IllegalStateException( "No SoundFontⓡ file interpreter service available")); try (var channel = FileChannel.open(this.path, READ)) { final var map = channel.map(READ_ONLY, 0L, channel.size()); final var parser = parsers.createForByteBuffer(this.path.toUri(), map); final var file_raw = parser.parse(); final var file = interpreters.createInterpreter(file_raw).interpret(); // Examine file.samples(), file.instruments(), and file.presets() }]]>
The jnoisetype package is designed to be extremely strict: Broken and invalid SoundFontⓡ files will be rejected with detailed diagnostic messages (typically, the name of the RIFF chunk containing the invalid data, the offset in bytes of the error, and possibly a link to the section of the specification that the input violated).
Using ServiceLoader is not required: The various providers in the jnoisetype package can be used via ServiceLoader, via OSGi declarative services, or simply instantiated manually. See the JavaDoc.
Writing
Use a builder to construct a description of the file to be written, and a writer to efficiently serialize the description into the required binary SoundFontⓡ form:
new IllegalStateException( "No SoundFontⓡ file builder service available")); final var writers = ServiceLoader.load(NTWriterProviderType.class) .findFirst() .orElseThrow(() -> new IllegalStateException( "No SoundFontⓡ file interpreter service available")); final var builder = builders.createBuilder(); // Set the various metadata used to describe the font. // Most fields are optional (see the JavaDoc and SoundFont specification). builder.setInfo( NTInfo.builder() .setName(NTShortString.of("Preset1")) .setSoundEngine(NTShortString.of("EMU8000")) .setVersion(NTVersion.of(2, 1)) .setEngineers(NTShortString.of("jnoisetype")) .setProduct(NTShortString.of("jnoisetype product")) .setCopyright(NTShortString.of("Public Domain")) .setComment(NTLongString.of("A comment.")) .setSoftware(NTShortString.of("Polyphone")) .build()); // Add a sample to the font. // The sizeOfWav method is assumed to return the size of the audio file in samples. // The copyWav method is assumed to copy data from the audio file to the given SeekableByteChannel. // // 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 audio. The jnoisetype package // will transparently add any zero-padding required by the SoundFontⓡ specification. final var sample0 = builder.addSample("000_60") .setSampleCount(sizeOfWav("000_60.wav")) .setSampleRate(22050) .setLoopEnd(8269L) .setDataWriter(channel -> copyWav("000_60.wav", channel)); // Create a new instrument. final var instrument0 = builder.addInstrument("instrument0"); // Create a zone for the instrument. As this is the first zone and contains // no generators or modulators, it is considered to be a "global" zone by // SoundFontⓡ interpreters. final var zone0 = instrument0.addZone(); // Create a zone containing a key range generator, a pan generator (panned to center), // and a sample generator that points to the audio file imported earlier. final var zone1 = instrument0.addZone() .addKeyRangeGenerator(0, 127) .addGenerator(NTGenerator.of(NTGeneratorOperatorIndex.of(17), "pan"), NTGenericAmount.of(0)) .addSampleGenerator(sample0); // Create a new preset. final var preset0 = builder.addPreset("preset0"); // Create a new "global" zone for the preset, as with the instrument created earlier. final var preset_zone0 = preset0.addZone(); // Create a new zone with a key range generator and a link to the instrument created earlier. final var preset_zone1 = preset0.addZone() .addKeyRangeGenerator(0, 127) .addInstrumentGenerator(instrument0); // Build an immutable description based on all of the information given above. final var description = builder.build(); // Create a new file and use a writer to serialize the builder description. // The writer functions specified in the description are called as the // file is serialized. try (var channel = FileChannel.open(path, CREATE, WRITE, TRUNCATE_EXISTING)) { final var writer = this.writers.createForChannel(path.toUri(), description, channel); writer.write(); }]]>
Command-line Tools
See the com.io7m.jnoisetype.cmdline-*-main.jar:
--pattern-include A regular expression matched against sample names. Matching names will be extracted if they are not subsequently excluded. Default: .* --verbose Set the minimum logging verbosity level Default: info Possible Values: [trace, debug, info, warn, error] ]]>
Currently, the command-line tool supports parsing and displaying a given SoundFontⓡ file as text, and extracting samples from SoundFontⓡ files.
JavaDoc
Maven
The following is a complete list of the project's modules expressed as Maven dependencies:
<dependency> <groupId>com.io7m.jnoisetype</groupId> <artifactId>com.io7m.jnoisetype</artifactId> <version>0.0.5</version> </dependency> <dependency> <groupId>com.io7m.jnoisetype</groupId> <artifactId>com.io7m.jnoisetype.api</artifactId> <version>0.0.5</version> </dependency> <dependency> <groupId>com.io7m.jnoisetype</groupId> <artifactId>com.io7m.jnoisetype.cmdline</artifactId> <version>0.0.5</version> </dependency> <dependency> <groupId>com.io7m.jnoisetype</groupId> <artifactId>com.io7m.jnoisetype.documentation</artifactId> <version>0.0.5</version> </dependency> <dependency> <groupId>com.io7m.jnoisetype</groupId> <artifactId>com.io7m.jnoisetype.parser.api</artifactId> <version>0.0.5</version> </dependency> <dependency> <groupId>com.io7m.jnoisetype</groupId> <artifactId>com.io7m.jnoisetype.tests</artifactId> <version>0.0.5</version> </dependency> <dependency> <groupId>com.io7m.jnoisetype</groupId> <artifactId>com.io7m.jnoisetype.vanilla</artifactId> <version>0.0.5</version> </dependency> <dependency> <groupId>com.io7m.jnoisetype</groupId> <artifactId>com.io7m.jnoisetype.writer.api</artifactId> <version>0.0.5</version> </dependency>
Each release of the project is made available on Maven Central within ten minutes of the release announcement.
Sources
This project uses Git to manage source code.
Repository: https://github.com/io7m/jnoisetype
$ git clone https://github.com/io7m/jnoisetype
License
Copyright © 2022 Mark Raynsford <code@io7m.com> https://www.io7m.com Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Bug Tracker
The project uses GitHub Issues to track issues.