Python API

This page is for using SampleToNES as a library in your own Python code. Use it to render instructions, generate a library, or run and export a reconstruction outside the application.

Names come from two packages:

  • The names in the table below come from sampletones: from sampletones import ....
  • The examples also use a few helpers from sampletones_core that are not in the table: write_wave, rendered_mix, ensure_library, DEFAULT_CHANNELS, and the FamiTracker instrument writers. Import them with the full path each example shows.

โ–Public surface

Name Purpose
Config generation configuration; build it with Config.load(path) or Config.default()
Window analysis window derived from a config (Window.from_config(config))
InstructionLibrary the library of candidate instructions a reconstruction searches
Reconstructor runs a reconstruction: Reconstructor(config, channels)("sample.wav")
Reconstruction the result of a reconstruction โ€” its per-channel instructions and the config used
ChannelName enum naming the four channels: pulse1, pulse2, triangle, noise
Generator shared base class of the oscillator generators
PulseGenerator, TriangleGenerator, NoiseGenerator render one channel's waveform from an instruction
Instruction shared base class of the per-frame channel instructions
PulseInstruction, TriangleInstruction, NoiseInstruction one channel's settings for a single frame

The package version is available as sampletones.__version__.

โ–Examples

Render an instruction to a waveform

from sampletones import Config, PulseGenerator, PulseInstruction
from sampletones_core.audio.io import write_wave

config = Config.load("config.json")

generator = PulseGenerator(config)
instruction = PulseInstruction(on=True, pitch=55, volume=7, duty_cycle=2)
audio = generator(instruction)

write_wave("pulse.wav", config.sample_rate, audio)

The output is a single G2 square wave one frame long.

Each generator keeps an oscillator phase and clock. A call renders a standalone waveform and leaves that state where it was. Pass save=True to advance the state into the next call, so a sequence of instructions renders as one continuous signal:

audio = generator(instruction, save=True)  # advances the generator state

Generate an instruction library

A reconstruction searches an instruction library built for its configuration by this version of SampleToNES, so prepare the library first:

from sampletones import Config
from sampletones_core.headless.library import ensure_library

config = Config.load("config.json")
ensure_library(config)  # builds the .ins library when it is missing or another version built it

The application's Instructions tab and sampletones library --config config.json do the same.

Reconstruct a sample

With a library in place for the configuration:

from sampletones import Config, Reconstructor
from sampletones_core.audio.io import write_wave
from sampletones_core.constants.enums import DEFAULT_CHANNELS
from sampletones_core.reconstructions.reconstruction.renders import rendered_mix

config = Config.load("config.json")

# The channels the run may use
reconstructor = Reconstructor(config, frozenset(DEFAULT_CHANNELS))

reconstruction = reconstructor("sample.wav")
reconstruction.save("reconstruction.stn")
write_wave("reconstruction.wav", config.sample_rate, rendered_mix(reconstruction))

Load a reconstruction

Reconstruction.load reads a saved .stn back into a Reconstruction, carrying its per-channel instructions and config:

from sampletones import Reconstruction

reconstruction = Reconstruction.load("reconstruction.stn")

Export instruments

Reconstruction.export returns the envelopes of each channel. build_instrument turns one channel's envelopes into a FamiTracker instrument, and write_fti saves it as an .fti file. repitched says whether a module's transpose rows reach the instrument, which a standalone file never has:

from sampletones import Reconstruction
from sampletones_core.formats.famitracker.builder import build_instrument
from sampletones_core.formats.famitracker.instrument import write_fti
from sampletones_core.formats.famitracker.specification.instruments import STANDALONE_INSTRUMENT_INDEX

reconstruction = Reconstruction.load("reconstruction.stn")

for channel, features in reconstruction.export().items():
    instrument = build_instrument(
        STANDALONE_INSTRUMENT_INDEX,
        channel.value,
        features,
        repitched=False
    )
    write_fti(f"{channel.value}.fti", instrument)

This writes one .fti per channel, named after the channel. A .ftm module comes from a project in the application. See FamiTracker formats.