ChiptuneSAK Class Reference

Intermediate Representation Classes

Chirp

Note

class chiptunesak.chirp.Note(start, note, duration, velocity=100, tied_from=False, tied_to=False)[source]

This class represents a note in human-friendly form: as a note with a start time, a duration, and a velocity.

note_num = None

MIDI note number

start_time = None

In ticks since tick 0

duration = None

In ticks

velocity = None

MIDI velocity 0-127

tied_from = None

Is the next note tied from this note?

tied_to = None

Is this note tied from the previous note?

split(tick_position)[source]

Splits a note into two notes at time tick_position, if the tick position falls within the note’s duration.

Parameters:tick_position (int) – position to split at
Returns:list with split note
Return type:list of Note

ChirpTrack

class chiptunesak.chirp.ChirpTrack(chirp_song, mchirp_track=None)[source]

This class represents a track (or a voice) from a song. It is basically a list of Notes with some other context information.

ASSUMPTION: The track contains notes for only ONE instrument (midi channel). Tracks with notes from more than one instrument will produce undefined results.

chirp_song = None

Parent song

name = None

Track name

channel = None

This track’s midi channel. Each track should have notes from only one channel.

notes = None

The notes in the track

program_changes = None

Program (patch) changes in the track

other = None

Other events in the track (includes voice changes and pitchwheel)

qticks_notes = None

Not start quantization from song

qticks_durations = None

Note duration quantization

import_mchirp_track(mchirp_track)[source]

Imports an MChirpTrack

Parameters:mchirp_track (MChirpTrack) – track to import
estimate_quantization()[source]

This method estimates the optimal quantization for note starts and durations from the note data itself. This version only uses the current track for the optimization. If the track is a part with long notes or not much movement, I recommend using the get_quantization() on the entire song instead. Many pieces have fairly well-defined note start spacing, but no discernable duration quantization, so in that case the default is half the note start quantization. These values are easily overridden.

Returns:tuple of quantization values for (start, duration)
Return type:tuple of ints
quantize(qticks_notes=None, qticks_durations=None)[source]

This method applies quantization to both note start times and note durations. If you want either to remain unquantized, simply specify either qticks parameter to be 1, so that it will quantize to the nearest tick (i.e. leave everything unchanged)

Parameters:
  • qticks_notes (int) – Resolution of note starts in ticks
  • qticks_durations (int) – Resolution of note durations in ticks. Also length of shortest note.
quantize_long(qticks)[source]

Quantizes only notes longer than 3/4 qticks; quantizes both start time and duration. This function is useful for quantization that also preserves some ornaments, such as grace notes.

Parameters:qticks (int) – Quantization for notes and durations
merge_notes(max_merge_length_ticks)[source]

Merges immediately adjacent notes if they are short and have the same note number.

Parameters:max_merge_length_ticks (int) – Length of the longest note to merge, in ticks
remove_short_notes(max_duration_ticks)[source]
Removes notes shorter than max_duration_ticks from the track.
Parameters:max_duration_ticks (int) – maximum duration of notes to remove, in ticks
set_min_note_len(min_len_ticks)[source]

Sets the minimum note length for the track. Notes shorter than min_len_ticks will be lengthened and any notes that overlap will have their start times adjusted to allow the new longer note.

Parameters:min_len_ticks (int) – Minimum note length
remove_polyphony()[source]

This function eliminates polyphony, so that in each channel there is only one note active at a time. If a chord is struck all at the same time, it will retain the highest note. Otherwise, when a new note is started, the previous note is truncated.

is_polyphonic()[source]

Returns whether the track is polyphonic; if any notes overlap it is.

Returns:True if track is polyphonic.
Return type:bool
is_quantized()[source]

Returns whether the current track is quantized or not. Since a quantization of 1 is equivalent to no quantization, a track quantized to tick will return False.

Returns:True if the track is quantized.
Return type:bool
remove_keyswitches(ks_max=8)[source]

Removes all MIDI notes with values less than or equal to ks_max. Some MIDI devices and applications use these extremely low notes to convey patch change or other information, so removing them (especially if you do not want polyphony) is a good idea.

Parameters:ks_max (int) – maximum note number for keyswitches in the track (often 8)
truncate(max_tick)[source]

Truncate the track to max_tick

Parameters:max_tick (int) – maximum tick number for events to start (track will play to end of any notes started)
transpose(semitones)[source]

Transposes track in-place by semitones, which can be positive (transpose up) or negative (transpose down)

Parameters:semitones – Number of semitones to transpose
modulate(num, denom)[source]

Modulates this track metrically by a factor of num / denom

Parameters:
  • num – Numerator of modulation
  • denom – Denominator of modulation
scale_ticks(scale_factor)[source]

Scales the ticks for this track by scale_factor.

Parameters:scale_factor
move_ticks(offset_ticks)[source]

Moves all the events in this track by offset_ticks. Any events that would have a time in ticks less than 0 are set to time zero.

Parameters:offset_ticks (int (signed)) –
set_program(program)[source]

Sets the default program (instrument) for the track at the start and removes any existing program changes.

Parameters:program (int) – program number

ChirpSong

class chiptunesak.chirp.ChirpSong(mchirp_song=None)[source]

Bases: chiptunesak.base.ChiptuneSAKBase

This class represents a song. It stores notes in an intermediate representation that approximates traditional music notation (as pitch-duration). It also stores other information, such as time signatures and tempi, in a similar way.

qticks_notes = None

Quantization for note starts, in ticks

qticks_durations = None

Quantization for note durations, in ticks

tracks = None

List of ChirpTrack tracks

other = None

List of all meta events that apply to the song as a whole

midi_meta_tracks = None

list of all the midi tracks that only contain metadata

midi_note_tracks = None

list of all the tracks that contain notes

time_signature_changes = None

List of time signature changes

key_signature_changes = None

List of key signature changes

tempo_changes = None

List of tempo changes

reset_all()[source]

Clear all tracks and reinitialize to default values

to_rchirp(**kwargs)[source]

Convert to RChirp. This calls the creation of an RChirp object

Returns:new RChirp object
Return type:rchirp.RChirpSong
to_mchirp(**kwargs)[source]

Convert to MChirp. This calls the creation of an MChirp object

Returns:new MChirp object
Return type:MChirpSong
import_mchirp_song(mchirp_song)[source]

Imports an MChirpSong

Parameters:mchirp_song (MChirpSong) –
set_metadata()[source]

Sets the song metadata to reflect the current status of the song. This function cleans up any redundant item signature, key signature, or tempo changes (two events that have the same timestamp) and keeps the last one it finds, then sets the metadata values to the first of each respectively.

estimate_quantization()[source]

This method estimates the optimal quantization for note starts and durations from the note data itself. This version all note data in the tracks. Many pieces have no discernable duration quantization, so in that case the default is half the note start quantization. These values are easily overridden.

quantize(qticks_notes=None, qticks_durations=None)[source]

This method applies quantization to both note start times and note durations. If you want either to remain unquantized, simply specify a qticks parameter to be 1 (quantization of 1 tick).

Parameters:
  • qticks_notes (int) – Quantization for note starts, in MIDI ticks
  • qticks_durations (int) – Quantization for note durations, in MIDI ticks
quantize_from_note_name(min_note_duration_string, dotted_allowed=False, triplets_allowed=False)[source]

Quantize song with more user-friendly input than ticks. Allowed quantizations are the keys for the constants.DURATION_STR dictionary. If an input contains a ‘.’ or a ‘-3’ the corresponding values for dotted_allowed and triplets_allowed will be overridden.

Parameters:
  • min_note_duration_string (str) – Quantization note value
  • dotted_allowed (bool) – If true, dotted notes are allowed
  • triplets_allowed (bool) – If true, triplets (of the specified quantization) are allowed
is_quantized()[source]

Has the song been quantized? This requires that all the tracks have been quantized with their current qticks_notes and qticks_durations values.

Returns:Boolean True if all tracks in the song are quantized
explode_polyphony(i_track)[source]

‘Explodes’ a single track into multi-track polyphony. The new tracks replace the old track in the song’s list of tracks, so later tracks will be pushed to higher indexes. The new tracks are named using the name of the original track with ‘_sx’ appended, where x is a number for the split notes. The polyphony is split using a first-available-track algorithm, which works well for splitting chords.

Parameters:i_track (int) – zero-based index of the track for the song (ignore the meta track - first track is 0)
remove_polyphony()[source]

Eliminate polyphony from all tracks.

is_polyphonic()[source]

Is the song polyphonic? Returns true if ANY of the tracks contains polyphony of any kind.

Returns:Boolean True if any track in the song is polyphonic
Return type:bool
remove_keyswitches(ks_max=8)[source]

Some MIDI programs use extremely low notes as a signaling mechanism. This method removes notes with pitch <= ks_max from all tracks.

Parameters:ks_max (int) – Maximum note number for the control notes
truncate(max_tick)[source]

Truncate the song to max_tick

Parameters:max_tick (int) – maximum tick number for events to start (song will play to end of any notes started)
transpose(semitones, minimize_accidentals=True)[source]

Transposes the song by semitones

Parameters:
  • semitones (int) – number of semitones to transpose by. Positive transposes to higher pitch.
  • minimize_accidentals (bool) – True to choose key signature to minimize number of accidentals
modulate(num, denom)[source]

This method performs metric modulation. It does so by multiplying the length of all notes by num/denom, and also automatically adjusts the time signatures and tempos such that the resulting music will sound identical to the original.

Parameters:
  • num (int) – Numerator of metric modulation
  • denom (int) – Denominator of metric modulation
scale_ticks(scale_factor)[source]

Scales the ticks for all events in the song. Multiplies the time for each event by scale_factor. This method also changes the ppq by the scale factor.

Parameters:scale_factor (float) – Floating-point scale factor to multiply all events.
move_ticks(offset_ticks)[source]

Moves all notes in the song a given number of ticks. Adds the offset to the current tick for every event. If the resulting event has a negative starting time in ticks, it is set to 0.

Parameters:offset_ticks (int) – Offset in ticks
set_qpm(qpm)[source]

Sets the tempo in QPM for the entire song. Any existing tempo events will be removed.

Parameters:qpm (int) – quarter-notes per minute tempo
set_time_signature(num, denom)[source]

Sets the time signature for the entire song. Any existing time signature changes will be removed.

Parameters:
  • num
  • denom
set_key_signature(new_key)[source]

Sets the key signature for the entire song. Any existing key signatures and changes will be removed.

Parameters:new_key (str) – Key signature. String such as ‘A#’ or ‘Abm’
end_time()[source]

Finds the end time of the last note in the song.

Returns:Time (in ticks) of the end of the last note in the song.
Return type:int
measure_starts()[source]

Returns the starting time for measures in the song. Calculated using time_signature_changes.

Returns:List of measure starting times in MIDI ticks
Return type:list
measures_and_beats()[source]

Returns the positions of all measures and beats in the song. Calculated using time_signature_changes.

Returns:List of MeasureBeat objects for each beat of the song.
Return type:list
get_measure_beat(time_in_ticks)[source]

This method returns a (measure, beat) tuple for a given time; the time is greater than or equal to the returned measure and beat but less than the next. The result should be interpreted as the time being during the measure and beat returned.

Parameters:time_in_ticks (int) – Time during the song, in MIDI ticks
Returns:MeasureBeat object with the current measure and beat
Return type:MeasureBeat
get_active_time_signature(time_in_ticks)[source]

Get the active time signature at a given time (in ticks) during the song.

Parameters:time_in_ticks (int) – Time during the song, in MIDI ticks
Returns:Active time signature at the time
Return type:TimeSignatureChange
get_active_key_signature(time_in_ticks)[source]

Get the active key signature at a given time (in ticks) during the song.

Parameters:time_in_ticks (int) – Time during the song, in MIDI ticks
Returns:Key signature active at the time
Return type:KeySignatureChange

MChirp

Rest

class chiptunesak.base.Rest(start_time, duration)

Triplet

class chiptunesak.base.Triplet(start_time=0, duration=0)[source]

Measure

class chiptunesak.mchirp.Measure(start_time, duration)[source]
process_triplets(measure_notes, ppq)[source]

Processes and accounts for all triplets in the measure

Parameters:
  • measure_notes (list of notes/triplets) – list of notes in the measure
  • ppq (int) – pulses per quarter from song
Returns:

new measure contents

Return type:

list of notes/triplet

populate_triplet(triplet, measure_notes)[source]

Given a triplet, populate it from the ntoes in the measure, splitting them if required

Parameters:
  • triplet (Triplet) – triplet to be populated
  • measure_notes (list of notes) – notes in the measure
Returns:

measure notes now including triplet

Return type:

list of notes/triplets

add_rests(measure_notes)[source]

Add rests to a measure content

Parameters:measure_notes (list of notes) – notes in the measure
Returns:new list of events including rests
Return type:list of events in measure
populate(track, carry=None)[source]

Populates a single measure with notes, rests, and other events.

Parameters:
  • track – Track from which events are to be imported
  • carry – If last note in previous measure is continued in this measure, the note with remaining time
Returns:

Carry note, if last note is to be carried into the next measure.

MChirpTrack

class chiptunesak.mchirp.MChirpTrack(mchirp_song, chirp_track=None)[source]
measures = None

List of measures in the track

name = None

Track name

channel = None

Midi channel number

mchirp_song = None

parent MChirpSong

qticks_notes = None

Inherit quantization from song

qticks_durations = None

Inherit quantization from song

import_chirp_track(chirp_track)[source]

Converts a track into measures, each of which is a sorted list of notes and other events

Parameters:chirp_track (ChirpTrack) – A ctsSongTrack that has been quantized and had polyphony removed
Returns:List of Measure objects corresponding to the measures

MChirpSong

class chiptunesak.mchirp.MChirpSong(chirp_song=None)[source]

Bases: chiptunesak.base.ChiptuneSAKBase

metadata = None

Metadata

qticks_notes = None

Quantization for note starts, in ticks

qticks_durations = None

Quantization for note durations, in ticks

other = None

Other MIDI events not used in measures

import_chirp_song(chirp_song)[source]

Gets all the measures from all the tracks in a song, and removes any empty (note-free) measures from the end.

Parameters:chirp_song (ChirpSong) – A chirp.ChirpSong song
trim()[source]

Trims all note-free measures from the end of the song.

trim_partial_measures()[source]

Trims any partial measures from the end of the file

get_time_signature(time_in_ticks)[source]

Finds the active key signature at a given time in the song

Parameters:time_in_ticks
Returns:The last time signature change event before the given time.
get_key_signature(time_in_ticks)[source]

Finds the active key signature at a given time in the song

Parameters:time_in_ticks
Returns:The last key signature change event before the given time.

RChirp

RChirpRow

class chiptunesak.rchirp.RChirpRow(row_num: int = None, milliframe_num: int = None, note_num: int = None, instr_num: int = None, new_instrument: int = None, gate: bool = None, milliframe_len: int = None, new_milliframe_tempo: int = None)[source]

The basic RChirp row

row_num = None

rchirp row number

milliframe_num = None

frames / 1000 since time 0

note_num = None

MIDI note number; None means no note asserted

instr_num = None

Instrument number

new_instrument = None

Indicates new instrument number; None means no change

gate = None

Gate on/off tri-value True/False/None; None means no gate change

milliframe_len = None

frames * 1000 to process this row (until next row)

new_milliframe_tempo = None

Indicates new tempo for channel (not global); None means no change

RChirpOrderEntry

class chiptunesak.rchirp.RChirpOrderEntry(pattern_num: int = None, transposition: int = 0, repeats: int = 1)[source]

RChirpOrderList

class chiptunesak.rchirp.RChirpOrderList[source]

An orderlist is a list of RChirpOrderEntry instances

RChirpPattern

class chiptunesak.rchirp.RChirpPattern(rows=None)[source]

A pattern made up of a set of rows

rows = None

List of RChirpRow instances (NOT a dictionary! No gaps allowed!)

RChirpVoice

class chiptunesak.rchirp.RChirpVoice(rchirp_song, chirp_track=None)[source]

The representation of a single voice; contains rows

rchirp_song = None

The song this voice belongs to

rows = None

dictionary: K:row num, V: RChirpRow instance

milliframe_indexed_rows

Returns dictionary of rows indexed by milliframe number

A voice holds onto a dictionary of rows keyed by row number. This method returns a dictionary of rows keyed by milliframe number.

Returns:A dictionary of rows keyed by milliframe number
Return type:defaultdict
sorted_rows

Returns a list of row-number sorted rows for the voice

Returns:A sorted list of RChirpRow instances
Return type:list
append_row(rchirp_row)[source]

Appends a row to the voice’s collection of rows

This is a helper method for treating rchirp like a list of contiguous rows, instead of a sparse dictionary of rows

Parameters:rchirp_row (RChirpRow) – A row to “append”
last_row

Returns the row with the largest milliframe number (latest in time)

Returns:row with latest milliframe number
Return type:RChirpRow
next_row_num

Returns one greater than the largest row number held onto by the voice

Returns:largest row number + 1
Return type:int
is_contiguous()[source]

Determines if the voice’s rows are contiguous. This function requires that row numbers are consecutive and that the corresponding milliframe numbers have no gaps.

Returns:True if rows are contiguous, False if not
Return type:bool
integrity_check()[source]

Finds problems with a voice’s row data

Returns:True if all integrity checks pass
Raises:AssertionError – Various integrity failure assertions possible
make_filled_rows()[source]

Creates a contiguous set of rows from a sparse row representation

Returns:filled rows
Return type:list of rows
orderlist_to_rows()[source]

Convert an orderlist with patterns into rows

Returns:rows
Return type:list of rows
validate_orderlist()[source]

Validate that the orderlist is self-consistent and generates the correct set of rows

Returns:True if consistent
Return type:bool
import_chirp_track(chirp_track)[source]
Imports a Chirp track into a raw RChirpVoice object. No compression or conversion to patterns
and orderlists performed. Track must be non-polyphonic and quantized.
Parameters:

chirp_track (ChirpTrack) – A chirp track

Raises:
  • ChiptuneSAKQuantizationError – Thrown if chirp track is not quantized
  • ChiptuneSAKPolyphonyError – Thrown if a single voice contains polyphony

RChirpSong

class chiptunesak.rchirp.RChirpSong(chirp_song=None)[source]

Bases: chiptunesak.base.ChiptuneSAKBase

The representation of an RChirp song. Contains voices, voice groups, and metadata.

arch = None

Architecture

voices = None

List of RChirpVoice instances

voice_groups = None

Voice groupings for lowering to multiple chips

patterns = None

Patterns to be shared among the voices

other = None

Other meta-events in song

compressed = None

Has song been through compression algorithm?

program_map = None

Midi-to-RChirp instrument map

metadata = None

Song metadata (author, copyright, etc.)

to_chirp(**kwargs)[source]

Converts the RChirpSong into a ChirpSong

Returns:Chirp song
Return type:ChirpSong
import_chirp_song(chirp_song)[source]

Imports a ChirpSong

Parameters:

chirp_song (ChirpSong) – A chirp song

Raises:
  • ChiptuneSAKQuantizationError – Thrown if chirp track is not quantized
  • ChiptuneSAKPolyphonyError – Thrown if a single voice contains polyphony
remove_tempo_changes()[source]

Removes tempo changes and sets milliframes_per_row constant for the entire song. This method is used to eliminate accelerandos and ritarandos throughout the song for better conversion to Chirp.

Returns:True on success
Return type:bool
has_patterns()[source]

Does this RChirp have patterns (and thus, presumably, orderlists)?

Returns:True if there are patterns
Return type:bool
make_program_map(chirp_song)[source]

Creates a program map of Chirp program numbers (patches) to instruments

Parameters:chirp_song (ChirpSong) – chirp song
Returns:program_map
Return type:dict of {chirp_program:rchirp_instrument}
is_contiguous()[source]

Determines if the voices’ rows are contiguous, without gaps in time

Returns:True if rows are contiguous, False if not
Return type:bool
integrity_check()[source]

Finds problems with voices’ row data

Returns:True if integrity checks pass for all voices
Raises:AssertionError – Various integrity failure assertions possible
set_row_delta_values()[source]

RChirpRow has some delta fields that are only set when there’s a change from previous rows.

This method goes through the rows, finds those changes and sets the appropriate fields

milliframe_indexed_voices()[source]

Returns a list of dicts, where many voices hold onto many rows. Rows indexed by milliframe number.

Returns:a list of dicts (voices->rows)
Return type:list
note_time_data_str()[source]

Returns a comma-separated value list representation of the rchirp data

Returns:CSV string
Return type:str
convert_to_chirp(**kwargs)[source]

Convert rchirp song to chirp

Returns:chirp conversion
Return type:ChirpSong

Input/Output Classes

MIDI Class

class chiptunesak.midi.MIDI[source]

Bases: chiptunesak.base.ChiptuneSAKIO

Import/Export MIDI files to and from Chirp songs.

The Chirp format is most closely tied to the MIDI standard. As a result, conversion between MIDI files and ChirpSong objects is one of the most common ways to import and export music using the ChiptuneSAK framework.

The MIDI class does not implement the standard to_bin() method because it uses the mido library to process low-level midi messages, and mido only deals with MIDI files.

The Chirp framework can import both MIDI type 0 and type 1 files. It will only write MIDI type 1 files.

to_chirp(filename, **kwargs)[source]

Import a midi file to Chirp format

Parameters:
  • filename (str) – filename to import
  • options
    • keyswitch (bool) Remove keyswitch notes with midi number <=8 (default True)
    • polyphony (bool) Allow polyphony (removal occurs after any quantization) (default True)
    • quantize (str)
      • ’auto’: automatically determines required quantization
      • ’8’, ‘16’, ‘32’, etc. : quantize to the named duration
Returns:

chirp song

Return type:

ChirpSong

to_file(song, filename, **kwargs)[source]

Exports a ChirpSong to a midi file.

Parameters:
  • song (chirpSong) – chirp song
  • filename (str) – filename for export
Returns:

True on success

Return type:

bool

midi_track_to_chirp_track(chirp_song, midi_track)[source]

Parse a MIDI track into notes, track name, and program changes. This method uses the mido library for MIDI messges within the track.

Parameters:midi_track (MIDO midi track) – midi track
import_midi_to_chirp(input_filename)[source]

Open and import a MIDI file into the ChirpSong representation. THis method can handle MIDI type 0 and 1 files.

param input_filename:
 MIDI filename.
get_meta(chirp_song, meta_track, is_zerotrack=False, is_metatrack=False)[source]

Process MIDI meta messages in a track.

param chirp_song:
 
param meta_track:
 
param is_zerotrack:
 
param is_metatrack:
 
split_midi_zero_into_tracks(midi_song)[source]

For MIDI Type 0 files, split the notes into tracks. To accomplish this, we move the metadata into Track 0 and then assign tracks 1-16 to the note data.

chirp_track_to_midi_track(chirp_track)[source]

Convert ChirpTrack to a midi track.

meta_to_midi_track(chirp_song)[source]

Exports metadata to a MIDI track.

export_chirp_to_midi(chirp_song, output_filename)[source]

Exports the song to a MIDI Type 1 file. Exporting to the midi format is privileged because this class is tied to many midi concepts and uses midid messages explicitly for some content.

GoatTracker Class

class chiptunesak.goat_tracker.GoatTracker[source]

Bases: chiptunesak.base.ChiptuneSAKIO

The IO interface for GoatTracker and GoatTracker Stereo

Supports conversions between RChirp and GoatTracker .sng format

set_options(**kwargs)[source]

Sets options for this module, with validation when required

Parameters:kwargs (keyword arguments) – keyword arguments for options
to_bin(rchirp_song, **kwargs)[source]

Convert an RChirpSong into a GoatTracker .sng file format

Parameters:
  • rchirp_song (MChirpSong) – rchirp data
  • options
    • end_with_repeat (bool) - True if song should repeat when finished
    • max_pattern_len (int) - Maximum pattern length to use. Must be <= 127
    • instruments (list of str) - Instrument names that will be extracted from GT instruments directory
      Note: These instruments are in instrument order, not in voice order! Multiple voices may use the same instrument, or multiple instruments may be on a voice. The instrument numbers are assigned in the order instruments are processed on conversion to RChirp.
Returns:

sng binary file format

Return type:

bytearray

to_file(rchirp_song, filename, **kwargs)[source]

Convert and save an RChirpSong as a GoatTracker sng file

Parameters:
  • rchirp_song (RChirpSong) – rchirp data
  • filename (str) – output path and file name
  • options – see to_bin()
to_rchirp(filename, **kwargs)[source]

Import a GoatTracker sng file to RChirp

Parameters:
  • filename (str) – File name of .sng file
  • options
    • subtune (int) - The subtune numer to import. Defaults to 0
    • arch (str) - architecture string. Must be one defined in constants.py
Returns:

rchirp song

Return type:

RChirpSong

SID Class

class chiptunesak.sid.SID[source]

Bases: chiptunesak.base.ChiptuneSAKIO

Parses and imports SIDs into RChirp using 6502/6510 emulation with a thin C64 layer.

This class is the import interface for ChiptuneSAK for SIDs. It runs the SID in the emulator, using the information in the SID header to configure the driver, and captures information from the interaction of the code with the SID chip(s) following init and play calls.

The resulting data can be converted to an RChirpSong object and/or written as a csv file that has a row for each invocation of the play routine. The csv file is useful for diagnosing how the play routine is modifying the SID chip and helps inform choices about the conversion of the SID music to the rchirp format.

set_options(**kwargs)[source]

Sets options for this module, with validation when required

Note: set_options gets called on __init__ (setting defaults), and a 2nd time if options are to be set after object instantiation.

Parameters:kwargs (keyword arguments) – keyword arguments for options

See to_rchirp() for possible options

capture()[source]

Captures data by emulating the SID song execution

This method calls internal methods that watch how the machine language program interacts with virtual SID chip(s), and records these interactions on a call-by-call basis (of the play routine).

Returns:captured SID data as a Dump object
Return type:Dump
to_rchirp(sid_in_filename, **kwargs)[source]

Converts a SID subtune into an RChirpSong

Parameters:
  • sid_in_filename (str) – SID input filename
  • options
    • subtune (int = 0) - subtune to extract (zero-indexed)
    • vibrato_cents_margin (int = 0) - cents margin to control snapping to previous note
    • tuning (int = CONCERT_A) - tuning to use,
    • seconds (float = 60) - seconds to capture
    • arch (string=’NTSC-C64’) - architecture. Note: overwritten if/when SID headers get parsed
    • gcf_row_reduce (bool = True) - reduce rows via GCF of row-activity gaps
    • create_gate_off_notes (bool = True) - allow new note starts when gate is off
    • assert_gate_on_new_note (bool = True) - True => gate on event in delta rows with new notes
    • always_include_freq (bool = False) - False => freq in delta rows only with new note
    • verbose (bool = True) - print details to stdout
Returns:

SID converted to RChirpSong

Return type:

RChirpSong

to_csv_file(output_filename, **kwargs)[source]

Convert a SID subtune into a CSV file

Each row of the csv file represents one call of the play routine.

Parameters:output_filename (str) – output CSV filename
get_val(val, format=None)[source]

Used to create CSV string values when not None

Parameters:
  • val (str or int) – str or int
  • format (str, optional) – format descriptor, defaults to None
Returns:

empty string, passed in value (with possible formatting)

Return type:

str or int

get_bool(bool, true_str='on', false_str='off')[source]

Used to create CSV string values when not None

Parameters:
  • bool (bool) – a boolean
  • true_str (str, optional) – string if true, defaults to ‘on’
  • false_str (str, optional) – string if false, defaults to ‘off’
Returns:

string description of boolean

Return type:

str

reduce_rows(sid_dump, rows_with_activity)[source]

The SidImport class samples SID chip state after each call to the play routine. This creates 1 row per play call. For non-multispeed, in most trackers, this would require speed 1 playback (1 frame per row), which cannot be achieved (again, without multispeed). So this method attempts to reduce the number of rows in the representaton. It does so by computing the greatest common divisor for the count of inactive rows between active rows, and then eliminates the unnecessary rows (while preserving rhythm structure).

# TODO: A row in cvs output contains all channels at a point in time. A row # in rchirp contains only one channel. When not making CVS output, better # results could be achieved by computing the GCD for each voice # independently.

Parameters:
  • sid_dump (sid.Dump) – Capture of SID chip state from the subtune
  • rows_with_activity (list of lists) – a list for each SID chip with a list of “active” row numbers
Returns:

the row granularity (the largest common factor across all periods of inactivity)

Return type:

int

Lilypond Class

class chiptunesak.lilypond.Lilypond[source]

Bases: chiptunesak.base.ChiptuneSAKIO

to_bin(mchirp_song, **kwargs)[source]

Exports MChirp to lilypond text

Parameters:
  • mchirp_song (MChirpSong) – song to export
  • options
    • format (string) - format, either ‘song’ or ‘clip’
    • autosort (bool) - sort tracks from highest to lowest average pitch
    • measures (list) - list of contiguous measures, from one track. Required for ‘clip’ format, ignored otherwise.
Returns:

lilypond text

Return type:

str

to_file(mchirp_song, filename, **kwargs)[source]

Exports MChirp to lilypond source file

Parameters:
  • mchirp_song (MChirpSong) – song to export
  • filename (str) – filename to write
  • options – see to_bin()
Returns:

lilypond text

Return type:

str

measure_to_lilypond(measure)[source]

Converts contents of a measure into Lilypond text

Parameters:measure – A ctsMeasure.Measure object
Returns:Lilypond text encoding the measure content.
export_clip_to_lilypond(mchirp_song, measures)[source]

Turns a set of measures into Lilypond suitable for use as a clip. All the music will be on a single line with no margins. It is recommended that this clip be turned into Lilypond using the command line:

lilypond -ddelete-intermediate-files -dbackend=eps -dresolution=600 -dpixmap-format=pngalpha --png <filename>

Parameters:
  • mchirp_song (MChirpSong) – ChirpSong from which the measures were taken.
  • measures (list) – List of measures.
Returns:

Lilypond markup ascii

Return type:

str

export_song_to_lilypond(mchirp_song)[source]

Converts a song to Lilypond format. Optimized for multi-page PDF output of the song. Recommended lilypond command:

lilypond <filename>

Parameters:mchirp_song (MChirpSong) – ChirpSong to convert to Lilypond format
Returns:Lilypond markup ascii
Return type:str

C128 Basic Class

class chiptunesak.c128_basic.C128Basic[source]

Bases: chiptunesak.base.ChiptuneSAKIO

The IO interface for C128BASIC Supports to_bin() and to_file() conversions from mchirp to C128 BASIC options: format, arch, instruments

set_options(**kwargs)[source]

Sets the options for commodore export

Parameters:kwargs (keyword arguments) – keyword arguments for options
to_bin(mchirp_song, **kwargs)[source]

Convert an MChirpSong into a C128 BASIC music program

Parameters:
  • mchirp_song (MChirpSong) – mchirp data
  • options – see to_file()
Returns:

C128 BASIC program

Return type:

str or bytearray

to_file(mchirp_song, filename, **kwargs)[source]

Converts and saves MChirpSong as a C128 BASIC music program

Parameters:
  • mchirp_song (MChirpSong) – mchirp data
  • filename (str) – path and filename
  • options
    • arch (str) - architecture name (see base for complete list)
    • format (str) - ‘bas’ for BASIC source code or ‘prg’ for prg
    • instruments (list of str) - list of 3 instruments for the three voices (in order).
      • Default is [‘piano’, ‘piano’, ‘piano’]
      • Supports the default C128 BASIC instruments: 0:’piano’, 1:’accordion’, 2:’calliope’, 3:’drum’, 4:’flute’, 5:’guitar’, 6:’harpsichord’, 7:’organ’, 8:’trumpet’, 9:’xylophone
    • tempo_override (int) - override the computed tempo
    • rem_override (string) - use passed string for leading REM statement instead of filename
export_mchirp_to_C128_BASIC(mchirp_song)[source]

Convert mchirp into a C128 Basic program that plays the song. This method is invoked via the C128Basic ChiptuneSAKIO class

Parameters:mchirp_song (MChirpSong) – An mchirp song
Returns:Returns an ascii BASIC program
Return type:str

ML64 Class

class chiptunesak.ml64.ML64[source]

Bases: chiptunesak.base.ChiptuneSAKIO

to_bin(song, **kwargs)[source]

Generates an ML64 string for a song

Parameters:
  • song (ChirpSong or mchirp.MChirpSong) – song
  • options
    • format (string) - ‘compact’, ‘standard’, or ‘measures’; ‘measures’ requires MChirp; the others convert from Chirp
Returns:

ML64 encoding of song

Return type:

str

to_file(song, filename, **kwargs)[source]

Writes ML64 to a file

Parameters:
Returns:

ML64 encoding of song

Return type:

str

export_chirp_to_ml64(chirp_song)[source]

Export song to ML64 format, with a minimum number of notes, either with or without measure comments. With measure comments, the comments appear within the measure but are not guaranteed to be exactly at the beginning of the measure, as tied notes will take precedence. In compact mode, the ML64 emitted is almost as small as possible. :param chirp_song: :type chirp_song:

export_mchirp_to_ml64(mchirp_song)[source]

Export the song in ML64 format, grouping notes into measures. The measure comments are guaranteed to appear at the beginning of each measure; tied notes will be split to accommodate the measure markers. :param mchirp_song: An mchirp song :type mchirp_song: MChirpSong

Compression Classes

One-Pass Class

class chiptunesak.one_pass_compress.OnePass[source]

Bases: chiptunesak.base.ChiptuneSAKCompress

find_best_repeats(repeats)[source]

Find the best repeats to use for a set of repeats. Right now, the metric is coverage, with the shortest repeats that give a certain coverage used, but the metric can easily be changed. :param repeats: list of valid repeats :type repeats: list of Repeat objects :return: list of optimal repeats :rtype: list of Repeat objects

apply_pattern(pattern_index, repeats, order)[source]

Given a pattern index and a set of repeats that match the pattern, mark the affected rows as used and insert them into the temporary orderlist :param pattern_index: Pattern number for the cstRChirpSong :type pattern_index: int :param repeats: Repeats that match the pattern :type repeats: list of Repeat objects :param order: temporary dictionary for the orderlist :type order: dictionary of (start_row, transposition) tuples :return: order :rtype: orderlist dictionary

trim_repeats(repeats)[source]

Trims the list of repeats to exclude rows that have been used. :param repeats: list of all repeats :type repeats: list of Repeat objects :return: list of valid repeats :rtype: list of Repeat objects

get_hole_lengths()[source]

Creates list of the holes of unused rows in a set of rows. :return: :rtype:

static add_rchirp_pattern_to_song(rchirp_song, pattern)[source]

Adds a pattern to an RChirpSong. It checks to be sue that the pattern has not been used. :param rchirp_song: An RChirpSong :type rchirp_song: rchirpSong :param pattern: the pattern to add to the song :type pattern: rchirp.RChirpPattern :return: Index of pattern :rtype: int

static make_orderlist(order)[source]

Converts the temporary dictionary-based orderlist into an RChirp-compatible orderlist :param order: dictionary orderlist (created internally) :type order: dictionary of (start_row, transposition) :return: orderlist to put into a rchirp.RChirpVoice :rtype: rchirp.RChirpOrderList

static validate_orderlist(patterns, order, total_length)[source]

Validates that the sparse orderlist is self-consistent. :param patterns: :type patterns: :param order: :type order: :return: :rtype: bool

One-Pass Global Class

class chiptunesak.one_pass_compress.OnePassGlobal[source]

Bases: chiptunesak.one_pass_compress.OnePass

Global greedy compression algorithm for GoatTracker

This algorithm attempts to find the best repeats to compress at every iteration; it begins by finding all possible repeats longer than min_pattern_length (which is O(n^2)) and then at each iteration chooses the set of repeats with the highest score. The rows used are removed and the algorithm iterates. At each iteration the available repeats are trimmed to avoid the used rows.

compress(rchirp_song, **kwargs)[source]

Compresses the RChirp using a single-pass global greedy pattern detection. It finds all repeats in the song and turns the lrgest one into a pattern. It continues this operation until the longest repeat is shorter than min_pattern_length, after which it fills in the gaps.

Parameters:
  • rchirp_song (rchirp.RChirpSong) – RChirp song to compress
  • options
    • min_pattern_length (int) - minimum pattern length in rows
    • min_transpose (int) - minimum transposition, in semitones, for a pattern to be a match (GoatTracker = -15)
    • max_transpose (int) - maximum transposition, in semitones, allowed for a pattern to be a match (GoatTracker = +14)
    • for no transposition, set both min_transpose and max_transpose to 0.
Returns:

rchirp_song with compression information added

Return type:

rchirp.RChirpSong

find_all_repeats(rows)[source]

Find every possible repeat in the rows longer than a minimum length :param rows: list of rows to search for repeats :type rows: list of cts.RChirpRows :return: list of all repeats found :rtype: list of Repeat

compress_global(rchirp_song)[source]

Global greedy compression algorithm for GoatTracker

This algorithm attempts to find the best repeats to compress at every iteration; it begins by finding all possible repeats longer than min_pattern_length (which is O(n^2)) and then at each iteration chooses the set of repeats with the highest score. The rows used are removed and the algorithm iterates. At each iteration the available repeats are trimmed to avoid the used rows.

Parameters:rchirp_song (rchirp.RChirpSong) – RChirp song to compress
Returns:rchirp_song with compression information added
Return type:rchirp.RChirpSong

One-Pass Left-to-Right Class

class chiptunesak.one_pass_compress.OnePassLeftToRight[source]

Bases: chiptunesak.one_pass_compress.OnePass

Left-to-right left single-pass compression for GoatTracker

This compression algorithm is the fastest; it can compress even the longest song in less than a second. It compresses the song in a manner similar to how a GoatTracker song would be constructed; starting from the beginning row, it finds the repeats of rows starting at that position that give the best score, and then moves to the first gap in the remaining rows and repeats. If the algorithm does not find any suitable repeats at a position, it moves to the next, and the unused rows are put into patterns after all the repeats have been found.

compress(rchirp_song, **kwargs)[source]

Compresses the RChirp using a single-pass left-to-right pattern detection. Starting at the first row, it finds the longest pattern that repeats, and if it is longer than min_pattern_length it removes the pattern and all repeats from the remaining rows. It then performs the same operation on the first available row until all patterns have been found, and then fills in the gaps.

Parameters:
  • rchirp_song (rchirp.RChirpSong) – RChirp song to compress
  • options
    • min_pattern_length (int) - minimum pattern length in rows
    • min_transpose (int) - minimum transposition, in semitones, for a pattern to be a match (GoatTracker = -15)
    • max_transpose (int) - maximum transposition, in semitones, allowed for a pattern to be a match (GoatTracker = +14)
    • for no transposition, set both min_transpose and max_transpose to 0.
Returns:

rchirp_song with compression information added

Return type:

rchirp.RChirpSong

compress_lr(rchirp_song)[source]

Right-to-left single-pass compression for GoatTracker

This compression algorithm is the fastest; it can compress even the longest song in less than a second. It compresses the song in a manner similar to how a GT song would be constructed; starting from the beginning row, it finds the repeats of rows starting at that position that give the best score, and then moves to the first gap in the remaining rows and repeats. If the algorithm does not find any suitable repeats at a position, it moves to the next, and the unused rows are put into patterns after all the repeats have been found.

Parameters:rchirp_song (rchirp.RChirpSong) – RChirp song to compress
Returns:rchirp_song with compression information added
Return type:rchirp.RChirpSong