From e63fdb47f027c1e97e3bb8944067d74fcb92beae Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 24 May 2026 01:05:38 -0400 Subject: [PATCH 01/22] WE'RE REWRITING THIS --- README.md | 70 ------------ requirements.txt | 1 - src/arcade/music.py | 246 ------------------------------------------- src/arcade/tracks.py | 213 ------------------------------------- src/converter.py | 55 ---------- src/main.py | 58 +--------- src/midi_to_song.py | 186 -------------------------------- 7 files changed, 1 insertion(+), 828 deletions(-) delete mode 100644 src/arcade/music.py delete mode 100644 src/arcade/tracks.py delete mode 100644 src/converter.py delete mode 100644 src/midi_to_song.py diff --git a/README.md b/README.md index a6e49fd..2287022 100644 --- a/README.md +++ b/README.md @@ -4,73 +4,3 @@ A Python tool to convert a MIDI file to a MakeCode Arcade song! (Work in progress) Some bug squashing may be needed but otherwise this tool is complete. - -Web version will be available soon in a different repo. - -## Install - -1. Download and install Python. -2. Clone this repo. -3. Install all the requirements in [`requirements.txt`](requirements.txt) - -> You may need to edit commands listed in this repo to use `py` or `python3` if -> `python` doesn't work. - -## Usage - -Run [`src/main.py`](src/main.py) at the root of the repository in the terminal. -(It is a CLI app) - -### Example commands - -To convert the MIDI file `Never_Gonna_Give_You_Up.mid` and print the Arcade -song to standard output with the default track "dog", no divisor, (divisor of -1), and no character break. - -```commandline -python src/main.py -i "Never_Gonna_Give_You_Up.mid" -``` - -To convert the MIDI file at the absolute path -`E:\Arcade MIDI to Song\testing\Friend_Like_Me_Disneys_Aladdin.mid` and -write the output to `Friend_Like_Me_Disneys_Aladdin song.ts` in the current -directory with the "computer" track, a divisor of 2, a character break of 512, -and with debug messages on. - -```commandline -python src/main.py -i "E:\Arcade MIDI to Song\testing\Friend_Like_Me_Disneys_Aladdin.mid" -o "Friend_Like_Me_Disneys_Aladdin song.ts" -d 2 -t computer -b 512 --debug -``` - -### Help text - -```commandline -usage: ArcadeMIDItoSong [-h] --input INPUT [--output OUTPUT] [--track TRACK] - [--divisor DIVISOR] [--break CHAR_BREAK] [--debug] - -A program to convert MIDI files to the Arcade song format. - -options: - -h, --help show this help message and exit - --input INPUT, -i INPUT - Input MIDI file - --output OUTPUT, -o OUTPUT - Output text file path, otherwise we will output to - standard output. - --track TRACK, -t TRACK - A track to use, which changes the instrument. - Available tracks include ['dog', 'duck', 'cat', - 'fish', 'car', 'computer', 'burger', 'cherry', - 'lemon']. (You can also use indices 0-8) Defaults to - 'dog'. - --divisor DIVISOR, -d DIVISOR - A divisor to reduce the number of measures used. A - higher integer means a longer song can fit in the - maximum of 255 measures of a song, but with less - precision. Must be greater than or equal to 1, and - defaults to 1 for no division. - --break CHAR_BREAK, -b CHAR_BREAK - Break the hex string after so many characters. - Defaults to 0 for no breaking. - --debug Include debug messages. Defaults to info and greater - severity messages only. -``` diff --git a/requirements.txt b/requirements.txt index eb8fd82..f7f28e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ mido -tqdm diff --git a/src/arcade/music.py b/src/arcade/music.py deleted file mode 100644 index c7a571d..0000000 --- a/src/arcade/music.py +++ /dev/null @@ -1,246 +0,0 @@ -# https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts - -import logging -from dataclasses import dataclass -from enum import Enum -from struct import pack -from typing import List, Optional - -from utils.logger import create_logger - -logger = create_logger(name=__name__, level=logging.INFO) - - -@dataclass -class Envelope: - attack: int - decay: int - sustain: int - release: int - amplitude: int - - -@dataclass -class LFO: - frequency: int - amplitude: int - - -@dataclass -class Instrument: - waveform: int - ampEnvelope: Envelope - pitchEnvelope: Optional[Envelope] = None - ampLFO: Optional[LFO] = None - pitchLFO: Optional[LFO] = None - octave: Optional[int] = None - - -@dataclass -class SongInfo: - measures: int - beatsPerMeasure: int - beatsPerMinute: int - ticksPerBeat: int - - -class EnharmonicSpelling(Enum): - NORMAL = "normal" - FLAT = "flat" - SHARP = "sharp" - - -@dataclass -class Note: - note: int - enharmonicSpelling: EnharmonicSpelling - - -@dataclass -class NoteEvent: - notes: List[Note] - startTick: int - endTick: int - - -@dataclass -class DrumSoundStep: - waveform: int - frequency: int - volume: int - duration: int - - -@dataclass -class DrumInstrument: - startFrequency: int - startVolume: int - steps: List[DrumSoundStep] - - -@dataclass -class Track: - instrument: Instrument - id: int - notes: List[NoteEvent] - name: Optional[str] = None - iconURI: Optional[str] = None - drums: Optional[List[DrumInstrument]] = None - - -@dataclass -class Song(SongInfo): - tracks: List[Track] - - -def get8BitNumber(num: Optional[int]) -> bytes: - return bytes([0 if num is None else num]) - - -def get16BitNumber(num: Optional[int]) -> bytes: - return pack(" bytes: - if isDrumTrack: - return bytes([note.note]) - - flags = 0 - if note.enharmonicSpelling == EnharmonicSpelling.FLAT: - flags = 1 - elif note.enharmonicSpelling == EnharmonicSpelling.SHARP: - flags = 2 - - note_val = (note.note - (instrumentOctave - 2) * 12) - note_val += 1 - 12 - byte_val = note_val | (flags << 6) - - if note_val > 63: - logger.warning( - f"Note {note.note} exceeds track range, skipping note!") - return bytes([]) - else: - try: - return bytes([byte_val]) - except ValueError: - logger.warning(f"Note {note.note} generates invalid byte value " - f"{byte_val}, skipping note!") - return bytes([]) - - -def encodeNoteEvent(event: NoteEvent, instrumentOctave: int, - isDrumTrack: bool) -> bytes: - out = bytearray() - out += get16BitNumber(event.startTick) - out += get16BitNumber(event.endTick) - out.append(len(event.notes)) - for note in event.notes: - out += encodeNote(note, instrumentOctave, isDrumTrack) - return out - - -def encodeInstrument(instrument: Instrument) -> bytes: - out = bytearray() - out.append(instrument.waveform) - out += get16BitNumber(instrument.ampEnvelope.attack) - out += get16BitNumber(instrument.ampEnvelope.decay) - out += get16BitNumber(instrument.ampEnvelope.sustain) - out += get16BitNumber(instrument.ampEnvelope.release) - out += get16BitNumber(instrument.ampEnvelope.amplitude) - if instrument.pitchEnvelope is None: - for _ in range(5): - out += get16BitNumber(0) - else: - out += get16BitNumber(instrument.pitchEnvelope.attack) - out += get16BitNumber(instrument.pitchEnvelope.decay) - out += get16BitNumber(instrument.pitchEnvelope.sustain) - out += get16BitNumber(instrument.pitchEnvelope.release) - out += get16BitNumber(instrument.pitchEnvelope.amplitude) - out.append(0 if instrument.ampLFO is None else instrument.ampLFO.frequency) - out += get16BitNumber( - 0 if instrument.ampLFO is None else instrument.ampLFO.amplitude) - out.append( - 0 if instrument.pitchLFO is None else instrument.pitchLFO.frequency) - out += get16BitNumber( - 0 if instrument.pitchLFO is None else instrument.pitchLFO.amplitude) - out.append(instrument.octave) - return out - - -def encodeMelodicTrack(track: Track) -> bytes: - encodedInstrument = encodeInstrument(track.instrument) - encodedNotes = [ - encodeNoteEvent(n, track.instrument.octave, False) for n in track.notes - ] - noteLength = sum([len(e) for e in encodedNotes]) - - out = bytearray() - out.append(track.id) - out.append(0) - out += get16BitNumber(len(encodedInstrument)) - out += encodedInstrument - out += get16BitNumber(noteLength) - for note in encodedNotes: - out += note - return out - - -def encodeTrack(track: Track) -> bytes: - if track.drums is not None: - raise NotImplementedError - # return encodeDrumTrack(track) - else: - return encodeMelodicTrack(track) - - -def encodeSong(song: Song) -> bytes: - encodedTracks = list( - map( - encodeTrack, - filter( - lambda track: len(track.notes) > 0, - song.tracks - ) - ) - ) - - out = bytearray() - out.append(0) - out += get16BitNumber(song.beatsPerMinute) - out.append(song.beatsPerMeasure) - out.append(song.ticksPerBeat) - out.append(song.measures) - out.append(len(encodedTracks)) - for track in encodedTracks: - out += track - return out - - -def getEmptySong(measures: int) -> Song: - return Song( - ticksPerBeat=8, - beatsPerMeasure=4, - beatsPerMinute=120, - measures=measures, - tracks=[ - Track( - id=0, name="Dog", notes=[], - iconURI="/static/music-editor/dog.png", - instrument=Instrument( - waveform=1, - octave=4, - ampEnvelope=Envelope( - attack=10, - decay=100, - sustain=500, - release=100, - amplitude=1024 - ), - pitchLFO=LFO( - frequency=5, - amplitude=0 - ) - ) - ) - ] - ) diff --git a/src/arcade/tracks.py b/src/arcade/tracks.py deleted file mode 100644 index f3f3274..0000000 --- a/src/arcade/tracks.py +++ /dev/null @@ -1,213 +0,0 @@ -from .music import Envelope, Instrument, LFO, Track - - -def get_available_tracks() -> list[Track]: - return [ - Track( - id=0, name="Dog", notes=[], - iconURI="/static/music-editor/dog.png", - instrument=Instrument( - waveform=1, - octave=4, - ampEnvelope=Envelope( - attack=10, - decay=100, - sustain=500, - release=100, - amplitude=1024 - ), - pitchLFO=LFO( - frequency=5, - amplitude=0 - ) - ) - ), - Track( - id=1, - name="Duck", notes=[], - iconURI="music-editor/duck.png", - instrument=Instrument( - waveform=15, - octave=4, - ampEnvelope=Envelope( - attack=5, - decay=530, - sustain=705, - release=450, - amplitude=1024 - ), - pitchEnvelope=Envelope( - attack=5, - decay=40, - sustain=0, - release=100, - amplitude=40 - ), - ampLFO=LFO( - frequency=3, - amplitude=20 - ), - pitchLFO=LFO( - frequency=6, - amplitude=2 - ) - ) - ), - Track( - id=2, - name="Cat", - notes=[], - iconURI="music-editor/cat.png", - instrument=Instrument( - waveform=12, - octave=5, - ampEnvelope=Envelope( - attack=150, - decay=100, - sustain=365, - release=400, - amplitude=1024 - ), - pitchEnvelope=Envelope( - attack=120, - decay=300, - sustain=0, - release=100, - amplitude=50 - ), - pitchLFO=LFO( - frequency=10, - amplitude=6 - ) - ) - ), - Track( - id=3, - name="Fish", - notes=[], - iconURI="music-editor/fish.png", - instrument=Instrument( - waveform=1, - octave=3, - ampEnvelope=Envelope( - attack=220, - decay=105, - sustain=1024, - release=350, - amplitude=1024 - ), - ampLFO=LFO( - frequency=5, - amplitude=100 - ), - pitchLFO=LFO( - frequency=1, - amplitude=4 - ) - ) - ), - Track( - id=4, - name="Car", - notes=[], - iconURI="music-editor/car.png", - instrument=Instrument( - waveform=16, - octave=4, - ampEnvelope=Envelope( - attack=5, - decay=100, - sustain=1024, - release=30, - amplitude=1024 - ), - pitchLFO=LFO( - frequency=10, - amplitude=4 - ) - ) - ), - Track( - id=5, - name="Computer", - notes=[], - iconURI="music-editor/computer.png", - instrument=Instrument( - waveform=15, - octave=2, - ampEnvelope=Envelope( - attack=10, - decay=100, - sustain=500, - release=10, - amplitude=1024 - ) - ) - ), - Track( - id=6, - name="Burger", - notes=[], - iconURI="music-editor/burger.png", - instrument=Instrument( - waveform=1, - octave=2, - ampEnvelope=Envelope( - attack=10, - decay=100, - sustain=500, - release=100, - amplitude=1024 - ) - ) - ), - Track( - id=7, - name="Cherry", - notes=[], - iconURI="music-editor/cherry.png", - instrument=Instrument( - waveform=2, - octave=3, - ampEnvelope=Envelope( - attack=10, - decay=100, - sustain=500, - release=100, - amplitude=1024 - ) - ) - ), - Track( - id=8, - name="Lemon", - notes=[], - iconURI="music-editor/lemon.png", - instrument=Instrument( - waveform=14, - octave=2, - ampEnvelope=Envelope( - attack=5, - decay=70, - sustain=870, - release=50, - amplitude=1024 - ), - pitchEnvelope=Envelope( - attack=10, - decay=45, - sustain=0, - release=100, - amplitude=20 - ), - ampLFO=LFO( - frequency=1, - amplitude=50 - ), - pitchLFO=LFO( - frequency=2, - amplitude=1 - ) - ) - ) - ] diff --git a/src/converter.py b/src/converter.py deleted file mode 100644 index c7869f0..0000000 --- a/src/converter.py +++ /dev/null @@ -1,55 +0,0 @@ -import logging -from enum import Enum -from typing import Optional, Union - -from mido import MidiFile - -from arcade.music import encodeSong -from midi_to_song import midi_to_song -from utils.logger import create_logger - -logger = create_logger(name=__name__, level=logging.DEBUG) - - -class OutputOptions(Enum): - MAKECODE_ARCADE_STRING = "makecode_arcade_string" - - -def convert(input: MidiFile, output: OutputOptions, - track: Optional[Union[int, str]] = None, - divisor: Optional[float] = 1, - char_break: Optional[int] = 0) -> Union[str]: - """ - Converts a MIDI file to a MakeCode Arcade song. - - :param input: Input MIDI file. - :param output: An OutputOptions enum value. Currently only supports - MAKECODE_ARCADE_STRING. - :param track: The track to use. Can be an index or a string. Defaults to - the first track. - :param divisor: A float to divide the number of measures used, to fit big songs - into the maximum number of measures being 255. Defaults to 1. - :param char_break: An integer to break the hex string after so many characters. - Defaults to 0. (No breaking) - :return: A string which is a MakeCode Arcade song. - """ - song = midi_to_song(input, int(track) if track.isnumeric() else track, - divisor) - bin_result = encodeSong(song) - - logger.debug(f"Generated {len(bin_result)} bytes, converting to text") - - logger.debug(f"Using character break of {char_break}") - - hex_result = map(lambda v: format(v, "02x"), bin_result) - result = "hex`" - for i, hex_num in enumerate(hex_result): - if char_break != 0 and i % char_break == 0: - result += "\n " - result += hex_num - if char_break != 0: - result += "\n" - result += "`" - logger.debug(f"Hex string result is {len(result)} characters long") - - return result diff --git a/src/main.py b/src/main.py index 256bd50..9cf6afe 100644 --- a/src/main.py +++ b/src/main.py @@ -2,42 +2,14 @@ from argparse import ArgumentParser from pathlib import Path -from mido import MidiFile - -from arcade.tracks import get_available_tracks -from converter import OutputOptions, convert from utils.logger import create_logger, set_all_stdout_logger_levels -tracks = get_available_tracks() -track_names = [t.name.lower() for t in tracks] -track_ids = [str(t.id) for t in tracks] - -parser = ArgumentParser(prog="ArcadeMIDItoSong", - description="A program to convert MIDI files to the " - "Arcade song format. ") +parser = ArgumentParser(description="Convert a MIDI file to a MakeCode Arcade song.") parser.add_argument("--input", "-i", required=True, type=Path, help="Input MIDI file") parser.add_argument("--output", "-o", type=Path, help="Output text file path, otherwise we will output to " "standard output.") -parser.add_argument("--track", "-t", metavar="TRACK", - choices=track_ids + track_names, - default=track_names[0], - help=f"A track to use, which changes the instrument. " - f"Available tracks include {track_names}. (You can " - f"also use indices 0-{len(track_ids) - 1}) Defaults " - f"to '{track_names[0]}'.") -parser.add_argument("--divisor", "-d", type=float, - default=1, - help="A divisor to reduce (or increase!) the number of " - "measures used. A higher float means a longer song " - "can fit in the maximum of 255 measures of a song, " - "but with less precision. Must be greater than 0, " - "defaults to 1 for no division.") -parser.add_argument("--break", "-b", type=int, dest="char_break", - default=0, - help="Break the hex string after so many characters. " - "Defaults to 0 for no breaking.") parser.add_argument("--debug", action="store_const", const=logging.DEBUG, default=logging.INFO, help="Include debug messages. Defaults to info and " @@ -46,31 +18,3 @@ logger = create_logger(name=__name__, level=logging.INFO) set_all_stdout_logger_levels(args.debug) logger.debug(f"Received arguments: {args}") - -input_path = Path(args.input) -logger.debug(f"Input path is {input_path}") - -midi = MidiFile(input_path) -logger.debug(f"MIDI is {midi.length}s long") - -divisor = float(args.divisor) -if not divisor > 0: - raise ValueError(f"divisor must be a float greater than 0, " - f"not {divisor}!") -logger.debug(f"Using divisor of {divisor}") - -char_break = int(args.char_break) -if char_break < 0: - raise ValueError(f"break must be an integer greater than or equal to 0, " - f"not {char_break}!") - -result = convert(midi, OutputOptions.MAKECODE_ARCADE_STRING, args.track, divisor, - char_break) - -output_path = args.output -if output_path is None: - logger.debug("No output path provided, printing to standard output") - print(result) -else: - logger.debug(f"Writing to {output_path}") - Path(output_path).write_text(result) diff --git a/src/midi_to_song.py b/src/midi_to_song.py deleted file mode 100644 index 86344c5..0000000 --- a/src/midi_to_song.py +++ /dev/null @@ -1,186 +0,0 @@ -import logging -from collections import namedtuple -from math import ceil -from sys import stdout -from typing import Union - -from mido import Message, MidiFile -from tqdm import tqdm - -from arcade.music import EnharmonicSpelling, Note, NoteEvent, Song, Track, \ - getEmptySong -from arcade.tracks import get_available_tracks -from utils.logger import create_logger - -logger = create_logger(name=__name__, level=logging.INFO) - - -def midi_to_song(midi: MidiFile, track_id: Union[str, int], - divisor: float) -> Song: - def get_track_from_name_or_id(name_or_id: Union[int, str]) -> Track: - logger.debug(f"Finding track {name_or_id}") - for track in get_available_tracks(): - if name_or_id == track.name.lower() or name_or_id == track.id: - selected_track = track - break - else: - raise ValueError(f"Unknown track ID or name {name_or_id}!") - logger.debug(f"Found track '{selected_track.name}' ({selected_track})") - return selected_track - - def find_note_time(start_index: int, note: int, - msgs: list[Message]) -> float: - time = 0 - for i in range(start_index, len(msgs)): - msg = msgs[i] - if msg.type not in ("note_on", "note_off"): - continue - time += msg.time - if ((msg.type == "note_on" and msg.velocity == 0) or - msg.type == "note_off") and msg.note == note: - break - return time - - NoteInfo = namedtuple("NoteInfo", - "note_value note_time start_tick end_tick") - - def gather_note_info(index: int, msgs: list[Message], - current_time: int) -> NoteInfo: - msg = msgs[index] - if msg.type != "note_on": - return NoteInfo( - note_value=-1, - note_time=-1, - start_tick=-1, - end_tick=-1 - ) - note_time = round(find_note_time(index + 1, msg.note, msgs) * 1000) - start_tick = round(current_time / 10) - end_tick = start_tick + round(note_time / 10) - return NoteInfo( - note_value=msg.note, - note_time=note_time, - start_tick=start_tick, - end_tick=end_tick - ) - - NoteSimpleEvent = namedtuple("NoteSimpleEvent", - "note start_tick end_tick") - ChordSimpleEvent = namedtuple("ChordSimpleEvent", - "notes start_tick end_tick") - - def find_chord_with_start_tick(chords: list[ChordSimpleEvent], - start_tick: int) -> int: - for i, chord in enumerate(chords): - if chord.start_tick == start_tick: - return i - return -1 - - def add_tracks_for_piano(song: Song, track_id: Union[int, str]): - selected_track = get_track_from_name_or_id(track_id) - selected_higher_track = get_track_from_name_or_id(track_id) - song.tracks.append(selected_track) - song.tracks[-1].instrument.octave = 2 - song.tracks.append(selected_higher_track) - song.tracks[-1].instrument.octave = 7 - logger.debug(f"Added 2 piano tracks") - - msgs = list(midi) - simple_notes = [] - - curr_time = 0 - ending_tick = 0 - for i, msg in tqdm(enumerate(msgs), desc="Processing (stage 1/3)", - file=stdout): - curr_time += round(msg.time * 1000) - if msg.type not in ("note_on", "note_off"): - continue - # logger.debug(f"{i}: {msg} (current time: {curr_time})") - if msg.type == "note_off" or ( - msg.type == "note_on" and msg.velocity == 0): - pass - else: - note_info = gather_note_info(i, msgs, curr_time) - note_simple_event = NoteSimpleEvent(note_info.note_value, - note_info.start_tick, - note_info.end_tick) - ending_tick = max(ending_tick, note_info.end_tick) - # duration = (note_simple_event.end_tick - - # note_simple_event.start_tick) - # logger.debug(f"{i}: * {note_simple_event} " - # f"(duration: {duration})") - simple_notes.append(note_simple_event) - - logger.debug(f"Last tick is {ending_tick} ({round(ending_tick / divisor)} " - f"after divisor)") - - ending_tick = round(ending_tick / divisor) - - simple_chords = [] - for note in tqdm(simple_notes, desc="Processing (stage 2/3)", file=stdout): - chord_index = find_chord_with_start_tick(simple_chords, - note.start_tick) - if chord_index == -1: - simple_chords.append( - ChordSimpleEvent([note.note], note.start_tick, note.end_tick) - ) - else: - simple_chords[chord_index].notes.append(note.note) - - ticks_per_beat = 100 - beats_per_measure = 10 - beats_per_minute = round(60 / divisor) - measure_count = ceil(ending_tick / ticks_per_beat / beats_per_measure) - - logger.debug( - f"{measure_count=} {ticks_per_beat=} {beats_per_measure=} {beats_per_minute=}") - logger.debug(f"Maximum number of ticks is " - f"{measure_count * ticks_per_beat * beats_per_measure} ticks") - - song = getEmptySong(measure_count) - song.ticksPerBeat = ticks_per_beat - song.beatsPerMeasure = beats_per_measure - song.beatsPerMinute = beats_per_minute - song.tracks.clear() - add_tracks_for_piano(song, track_id) - - for i, chord in tqdm(enumerate(simple_chords), desc="Processing (stage 3/3)", - file=stdout): - # logger.debug(f"Chord {i}: {chord}") - all_notes = [Note(note=n, enharmonicSpelling=EnharmonicSpelling.NORMAL) - for n in chord.notes] - notes = [] - higher_notes = [] - for note in all_notes: - instrumentOctave = song.tracks[-2].instrument.octave - note_val = (note.note - (instrumentOctave - 2) * 12) - note_val += 1 - 12 - if note_val > 63: - higher_notes.append(note) - else: - notes.append(note) - event = NoteEvent( - notes=notes, - startTick=round(chord.start_tick / divisor), - endTick=round(chord.end_tick / divisor) - ) - higher_event = NoteEvent( - notes=higher_notes, - startTick=round(chord.start_tick / divisor), - endTick=round(chord.end_tick / divisor) - ) - # logger.debug(f"Note event {i}: {event}") - if len(event.notes) > 0: - song.tracks[-2].notes.append(event) - if len(higher_event.notes) > 0: - song.tracks[-1].notes.append(higher_event) - ending_tick = chord.end_tick - - for i, track in enumerate(song.tracks): - logger.debug( - f"Created {len(song.tracks[i].notes)} note events in track {i}") - logger.debug( - f"Total of {sum([len(t.notes) for t in song.tracks])} note events") - logger.debug(f"Last tick is {ending_tick}") - - return song From cba1cba11a448c451e4d7b896bd82cf6a80a053d Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 24 May 2026 23:13:48 -0400 Subject: [PATCH 02/22] Port over music.ts and pxtmusic.d.ts to Python --- src/arcade/music.py | 526 ++++++++++++++++++++++++++++++++++++++ src/arcade/music_types.py | 169 ++++++++++++ 2 files changed, 695 insertions(+) create mode 100644 src/arcade/music.py create mode 100644 src/arcade/music_types.py diff --git a/src/arcade/music.py b/src/arcade/music.py new file mode 100644 index 0000000..a1d0d42 --- /dev/null +++ b/src/arcade/music.py @@ -0,0 +1,526 @@ +# https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts +import struct +from typing import Tuple + +from arcade.music_types import * +from utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +def set8BitNumber(buf: bytearray, offset: int, value: int): + struct.pack_into(" int: + return struct.unpack_from(" int: + return struct.unpack_from(" bytearray: + """ + Encode a MakeCode Arcade song into a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L96 + + :param song: The MakeCode Arcade song. + :return: A bytearray, convert this to hex to use in a MakeCode Arcade program. + """ + encodedTracks = [encodeTrack(track) for track in song.tracks if + len(track.notes) > 0] + encodedTrackVelocities: List[bytearray] = list(filter(lambda v: v is not None, + [encodeTrackVelocity(track) + for track in + song.tracks])) + + trackLength = sum(len(c) for c in (encodedTracks + encodedTrackVelocities)) + + out = bytearray(7 + trackLength) + set8BitNumber(out, 0, 0) # encoding version + set16BitNumber(out, 1, song.beatsPerMinute) + set8BitNumber(out, 3, song.beatsPerMeasure) + set8BitNumber(out, 4, song.ticksPerBeat) + set8BitNumber(out, 5, song.measures) + set8BitNumber(out, 6, len(encodedTracks)) + + current = 7 + for track in encodedTracks: + out[current:current + len(track)] = track + current += len(track) + + for trackVelocity in encodedTrackVelocities: + out[current:current + len(trackVelocity)] = trackVelocity + current += len(trackVelocity) + + return out + + +def encodeInstrument(instrument: Instrument) -> bytearray: + """ + Encode a MakeCode Arcade instrument into a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L127 + + :param instrument: The MakeCode Arcade instrument. + :return: A bytearray. + """ + out = bytearray(28) + set8BitNumber(out, 0, instrument.waveform) + set16BitNumber(out, 1, instrument.ampEnvelope.attack) + set16BitNumber(out, 3, instrument.ampEnvelope.decay) + set16BitNumber(out, 5, instrument.ampEnvelope.sustain) + set16BitNumber(out, 7, instrument.ampEnvelope.release) + set16BitNumber(out, 9, instrument.ampEnvelope.amplitude) + if instrument.pitchEnvelope is not None: + set16BitNumber(out, 11, instrument.pitchEnvelope.attack) + set16BitNumber(out, 13, instrument.pitchEnvelope.decay) + set16BitNumber(out, 15, instrument.pitchEnvelope.sustain) + set16BitNumber(out, 17, instrument.pitchEnvelope.release) + set16BitNumber(out, 19, instrument.pitchEnvelope.amplitude) + else: + set16BitNumber(out, 11, 0) + set16BitNumber(out, 13, 0) + set16BitNumber(out, 15, 0) + set16BitNumber(out, 17, 0) + set16BitNumber(out, 19, 0) + if instrument.ampLFO is not None: + set8BitNumber(out, 21, instrument.ampLFO.frequency) + set16BitNumber(out, 22, instrument.ampLFO.amplitude) + else: + set8BitNumber(out, 21, 0) + set16BitNumber(out, 22, 0) + if instrument.pitchLFO is not None: + set8BitNumber(out, 24, instrument.pitchLFO.frequency) + set16BitNumber(out, 25, instrument.pitchLFO.amplitude) + else: + set8BitNumber(out, 24, 0) + set16BitNumber(out, 25, 0) + set8BitNumber(out, 27, instrument.octave if instrument.octave is not None else 0) + return out + + +def encodeDrumInstrument(drum: DrumInstrument) -> bytearray: + """ + Encode a MakeCode Arcade drum instrument into a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L149 + + :param drum: The MakeCode Arcade drum instrument. + :return: A bytearray. + """ + out = bytearray(5 + 7 * len(drum.steps)) + set8BitNumber(out, 0, len(drum.steps)) + set16BitNumber(out, 1, drum.startFrequency) + set16BitNumber(out, 3, drum.startVolume) + for i, step in enumerate(drum.steps): + start = 5 + i * 7 + set8BitNumber(out, start, step.waveform) + set16BitNumber(out, start + 1, step.frequency) + set16BitNumber(out, start + 3, step.volume) + set16BitNumber(out, start + 5, step.duration) + return out + + +def encodeNoteEvent(event: NoteEvent, instrumentOctave: int, + isDrumTrack: bool) -> bytearray: + """ + Encode a MakeCode Arcade note event into a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L166 + + :param event: The MakeCode Arcade `NoteEvent`. + :param instrumentOctave: The instrument octave offset used for the note event. + :param isDrumTrack: Whether this note event is for a drum track or not. + :return: A bytearray. + """ + out = bytearray(5 + len(event.notes)) + set16BitNumber(out, 0, event.startTick) + set16BitNumber(out, 2, event.endTick) + set8BitNumber(out, 4, len(event.notes)) + + for i, note in enumerate(event.notes): + set8BitNumber(out, 5 + i, encodeNote(note, instrumentOctave, isDrumTrack)) + + return out + + +def encodeNote(note: Note, instrumentOctave: int, isDrumTrack: bool) -> int: + """ + Encode a MakeCode Arcade note into a single byte. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L179 + + :param note: The MakeCode Arcade `Note`. + :param instrumentOctave: The instrument octave offset used for the note. + :param isDrumTrack: Whether this note is for a drum track or not. + :return: An int, which will fit into a single byte. + """ + if isDrumTrack: + return note.note + + flags = 0 + if note.enharmonicSpelling == EnharmonicSpelling.FLAT: + flags = 1 + elif note.enharmonicSpelling == EnharmonicSpelling.SHARP: + flags = 2 + + return (note.note - (instrumentOctave - 2) * 12) | (flags << 6) + + +def encodeTrack(track: Track) -> bytearray: + """ + Encode a MakeCode Arcade track into a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L195 + + :param track: The MakeCode Arcade `Track`. + :return: A bytearray. + """ + if track.drums: + return encodeDrumTrack(track) + else: + return encodeMelodicTrack(track) + + +def encodeTrackVelocity(track: Track) -> Optional[bytearray]: + """ + Encode a MakeCode Arcade track's velocity data into a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L200 + + :param track: The MakeCode Arcade `Track`. + :return: A bytearray. + """ + if not any([note.velocity is not None and note.velocity < 128 for note in + track.notes]): + return None + + out = bytearray(1 + len(track.notes)) + set8BitNumber(out, 0, track.id) + for i, note in enumerate(track.notes): + set8BitNumber(out, 1 + i, note.velocity if note.velocity is not None else 0) + return out + + +def encodeMelodicTrack(track: Track) -> bytearray: + """ + Encode a MakeCode Arcade melodic track into a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L211 + + :param track: The MakeCode Arcade `Track`, must be melodic. + :return: A bytearray. + """ + encodedInstrument = encodeInstrument(track.instrument) + encodedNotes = [encodeNoteEvent(note, track.instrument.octave, False) for note in + track.notes] + noteLength = sum(len(c) for c in encodedNotes) + + out = bytearray(6 + len(encodedInstrument) + noteLength) + set8BitNumber(out, 0, track.id) + set8BitNumber(out, 1, 0) + + set16BitNumber(out, 2, len(encodedInstrument)) + current = 4 + out[current:current + len(encodedInstrument)] = encodedInstrument + current += len(encodedInstrument) + + set16BitNumber(out, current, noteLength) + current += 2 + for note in encodedNotes: + out[current:current + len(note)] = note + current += len(note) + + return out + + +def encodeDrumTrack(track: Track) -> bytearray: + """ + Encode a MakeCode Arcade drum track into a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L235 + + :param track: The MakeCode Arcade `Track`, must be a drum track. + :return: A bytearray. + """ + assert track.drums is not None + encodedDrums = [encodeDrumInstrument(drum) for drum in track.drums] + drumLength = sum(len(c) for c in encodedDrums) + + encodedNotes = [encodeNoteEvent(note, 0, True) for note in track.notes] + noteLength = sum(len(c) for c in encodedNotes) + + out = bytearray(6 + drumLength + noteLength) + set8BitNumber(out, 0, track.id) + set8BitNumber(out, 1, 1) + set16BitNumber(out, 2, drumLength) + current = 4 + + for drum in encodedDrums: + out[current:current + len(drum)] = drum + current += len(drum) + + set16BitNumber(out, current, noteLength) + current += 2 + for note in encodedNotes: + out[current:current + len(note)] = note + current += len(note) + + return out + + +def decodeSong(buf: bytearray) -> Song: + """ + Decode a MakeCode Arcade song from a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L269 + + :param buf: A bytearray of an entire song. + :return: A MakeCode Arcade `Song`. + """ + res = Song(beatsPerMinute=get16BitNumber(buf, 1), + beatsPerMeasure=get8BitNumber(buf, 3), + ticksPerBeat=get8BitNumber(buf, 4), + measures=get8BitNumber(buf, 5), + tracks=[]) + + numTracks = get8BitNumber(buf, 6) + current = 7 + + for _ in range(numTracks): + track, pointer = decodeTrack(buf, current) + current = pointer + res.tracks.append(track) + + while current < len(buf): + current = decodeTrackVelocity(buf, res.tracks, current) + + return res + + +def decodeInstrument(buf: bytearray, offset: int) -> Instrument: + """ + Decode a MakeCode Arcade instrument from a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L294 + + :param buf: A bytearray of an entire song. + :param offset: The offset in the bytearray which to start reading the instrument + data from. + :return: A MakeCode Arcade `Instrument`. + """ + return Instrument( + waveform=get8BitNumber(buf, offset), + ampEnvelope=Envelope( + attack=get16BitNumber(buf, offset + 1), + decay=get16BitNumber(buf, offset + 3), + sustain=get16BitNumber(buf, offset + 5), + release=get16BitNumber(buf, offset + 7), + amplitude=get16BitNumber(buf, offset + 9), + ), + pitchEnvelope=Envelope( + attack=get16BitNumber(buf, offset + 11), + decay=get16BitNumber(buf, offset + 13), + sustain=get16BitNumber(buf, offset + 15), + release=get16BitNumber(buf, offset + 17), + amplitude=get16BitNumber(buf, offset + 19), + ), + ampLFO=LFO( + frequency=get8BitNumber(buf, offset + 21), + amplitude=get16BitNumber(buf, offset + 22), + # the original implementation is 22 instead of offset + 22 + ), + pitchLFO=LFO( + frequency=get8BitNumber(buf, offset + 24), + amplitude=get16BitNumber(buf, offset + 25), + # the original implementation is 25 instead of offset + 25 + ), + octave=get8BitNumber(buf, offset + 27), + ) + + +def decodeTrack(buf: bytearray, offset: int) -> Tuple[Track, int]: + """ + Decode a MakeCode Arcade track from a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L323 + + :param buf: A bytearray of an entire song. + :param offset: The offset in the bytearray which to start reading the track + data from. + :return: A MakeCode Arcade `Track`. + """ + if get8BitNumber(buf, offset + 1) != 0: + return decodeDrumTrack(buf, offset) + else: + return decodeMelodicTrack(buf, offset) + + +def decodeTrackVelocity(buf: bytearray, tracks: List[Track], offset: int) -> int: + """ + Decode a MakeCode Arcade track velocity from a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L331 + + :param buf: A bytearray of an entire song. + :param tracks: A list of `Track` objects which have already been decoded from the + bytearray, the one with the matching ID will have the note velocities set. + :param offset: The offset in the bytearray which to start reading the track velocity + data from. + :return: The next offset after the end of this track velocity data. + """ + trackId = get8BitNumber(buf, offset) + track = next((t for t in tracks if t.id == trackId), None) + if track is None: + raise ValueError(f"Track with {trackId} not found") + for i in range(len(track.notes)): + track.notes[i].velocity = get8BitNumber(buf, offset + i + 1) + return offset + len(track.notes) + 1 + + +def decodeDrumInstrument(buf: bytearray, offset: int) -> DrumInstrument: + """ + Decode a MakeCode Arcade drum instrument from a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L341 + + :param buf: A bytearray of an entire song. + :param offset: The offset in the bytearray which to start reading the drum + instrument data from. + :return: A MakeCode Arcade `DrumInstrument`. + """ + res = DrumInstrument( + startFrequency=get16BitNumber(buf, offset + 1), + startVolume=get16BitNumber(buf, offset + 3), + steps=[], + ) + + for i in range(get8BitNumber(buf, offset)): + start = offset + 5 + i * 7 + res.steps.append(DrumSoundStep( + waveform=get8BitNumber(buf, start), + frequency=get16BitNumber(buf, start + 1), + volume=get16BitNumber(buf, start + 3), + duration=get16BitNumber(buf, start + 5) + )) + + return res + + +def decodeNoteEvent(buf: bytearray, offset: int, instrumentOctave: int, + isDrumTrack: bool) -> NoteEvent: + """ + Decode a MakeCode Arcade note event from a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L361 + + :param buf: A bytearray of an entire song. + :param offset: The offset in the bytearray which to start reading the note event + data from. + :param instrumentOctave: The instrument's octave offset used for the note event, + needed to decode the note value itself. + :param isDrumTrack: Whether this note event is for a drum track or not, needed to + decode the note. + :return: A MakeCode Arcade `NoteEvent`. + """ + res = NoteEvent( + startTick=get16BitNumber(buf, offset), + endTick=get16BitNumber(buf, offset + 2), + notes=[], + ) + + for i in range(get8BitNumber(buf, offset + 4)): + res.notes.append( + decodeNote( + get8BitNumber(buf, offset + 5 + i), + instrumentOctave, + isDrumTrack + ) + ) + + return res + + +def decodeNote(note: int, instrumentOctave: int, isDrumTrack: bool) -> Note: + """ + Construct a MakeCode Arcade note from specified parameters. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L374 + + :param note: The note number itself, between 0 and 63. For melodic tracks, the + instrument octave is taken into account. + :param instrumentOctave: The track's instrument's octave offset. + :param isDrumTrack: Whether this note is for a drum track or not. + :return: A MakeCode Arcade `Note`. + """ + flags = note >> 6 + res = Note( + note=note if isDrumTrack else ((note & 0x3F) + (instrumentOctave - 2) * 12), + enharmonicSpelling=EnharmonicSpelling.NORMAL + ) + + if flags == 1: + res.enharmonicSpelling = EnharmonicSpelling.FLAT + elif flags == 2: + res.enharmonicSpelling = EnharmonicSpelling.SHARP + + return res + + +def decodeMelodicTrack(buf: bytearray, offset: int) -> Tuple[Track, int]: + """ + Decode a MakeCode Arcade melodic track from a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L392 + + :param buf: A bytearray of an entire song. + :param offset: The offset in the bytearray which to start reading the melodic track + data from. + :return: A tuple of a MakeCode Arcade `Track` and the next offset after the end of + this track data. + """ + res = Track( + id=get8BitNumber(buf, offset), + instrument=decodeInstrument(buf, offset + 4), + notes=[] + ) + + noteStart = offset + 4 + get16BitNumber(buf, offset + 2) + noteLength = get16BitNumber(buf, noteStart) + + currentOffset = noteStart + 2 + + while currentOffset < noteStart + 2 + noteLength: + res.notes.append( + decodeNoteEvent(buf, currentOffset, res.instrument.octave, False) + ) + currentOffset += 5 + len(res.notes[-1].notes) + + return res, currentOffset + + +def decodeDrumTrack(buf: bytearray, offset: int) -> Tuple[Track, int]: + """ + Decode a MakeCode Arcade drum track from a bytearray. Ported from + https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L412 + + :param buf: A bytearray of an entire song. + :param offset: The offset in the bytearray which to start reading the drum track + data from. + :return: A tuple of a MakeCode Arcade `Track` and the next offset after the end of + this track data. + """ + res = Track( + id=get8BitNumber(buf, offset), + instrument=Instrument( + ampEnvelope=Envelope(attack=0, decay=0, sustain=0, release=0, amplitude=0), + waveform=0, octave=0), + notes=[], + drums=[] + ) + + drumByteLength = get16BitNumber(buf, offset + 2) + currentOffset = offset + 4 + + while currentOffset < (offset + 4 + drumByteLength): + res.drums.append(decodeDrumInstrument(buf, currentOffset)) + currentOffset += 5 + 7 * len(res.drums[-1].steps) + + noteLength = get16BitNumber(buf, currentOffset) + currentOffset += 2 + + while currentOffset < (offset + 4 + drumByteLength + noteLength): + res.notes.append(decodeNoteEvent(buf, currentOffset, 0, True)) + currentOffset += 5 + len(res.notes[-1].notes) + + return res, currentOffset diff --git a/src/arcade/music_types.py b/src/arcade/music_types.py new file mode 100644 index 0000000..fca48f9 --- /dev/null +++ b/src/arcade/music_types.py @@ -0,0 +1,169 @@ +# https://github.com/microsoft/pxt/blob/master/localtypings/pxtmusic.d.ts +from __future__ import annotations + +import logging +from dataclasses import dataclass +from enum import Enum +from typing import List, Optional + +from utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +# /** +# * Byte encoding format for songs +# * FIXME: should this all be word aligned? +# * +# * song(7 + length of all tracks bytes) +# * 0 version +# * 1 beats per minute +# * 3 beats per measure +# * 4 ticks per beat +# * 5 measures +# * 6 number of tracks +# * ...tracks +# * ...track velocities +# * +# * track(6 + instrument length + note length bytes) +# * 0 id +# * 1 flags +# * 2 instruments byte length +# * 4...instrument +# * notes byte length +# * ...note events +# * +# * instrument(28 bytes) +# * 0 waveform +# * 1 amp attack +# * 3 amp decay +# * 5 amp sustain +# * 7 amp release +# * 9 amp amp +# * 11 pitch attack +# * 13 pitch decay +# * 15 pitch sustain +# * 17 pitch release +# * 19 pitch amp +# * 21 amp lfo freq +# * 22 amp lfo amp +# * 24 pitch lfo freq +# * 25 pitch lfo amp +# * 27 octave +# * +# * drum(5 + 7 * steps bytes) +# * 0 steps +# * 1 start freq +# * 3 start amp +# * 5...steps +# * +# * drum step(7 bytes) +# * 0 waveform +# * 1 freq +# * 3 volume +# * 5 duration +# * +# * note event(5 + 1 * polyphony bytes) +# * 0 start tick +# * 2 end tick +# * 4 polyphony +# * 5...notes(1 byte each) +# * +# * note (1 byte) +# * lower six bits = note - (instrumentOctave - 2) * 12 +# * upper two bits are the enharmonic spelling: +# * 0 = normal +# * 1 = flat +# * 2 = sharp +# * +# * track velocity +# * 0 track id +# * 1...velocities +# * +# * velocty +# * 1 byte +# */ + + +@dataclass +class Instrument: + waveform: int + ampEnvelope: Envelope + octave: int + pitchEnvelope: Optional[Envelope] = None + ampLFO: Optional[LFO] = None + pitchLFO: Optional[LFO] = None + + +@dataclass +class Envelope: + attack: int + decay: int + sustain: int + release: int + amplitude: int + + +@dataclass +class LFO: + frequency: int + amplitude: int + + +@dataclass +class SongInfo: + measures: int + beatsPerMeasure: int + beatsPerMinute: int + ticksPerBeat: int + + +@dataclass +class Song(SongInfo): + tracks: List[Track] + + +@dataclass +class Track: + id: int + instrument: Instrument + notes: List[NoteEvent] + drums: Optional[List[DrumInstrument]] = None + name: Optional[str] = None + iconURI: Optional[str] = None + + +@dataclass +class NoteEvent: + notes: List[Note] + startTick: int + endTick: int + velocity: Optional[int] = None + + +class EnharmonicSpelling(Enum): + NORMAL = "normal" + FLAT = "flat" + SHARP = "sharp" + + +@dataclass +class Note: + note: int + enharmonicSpelling: EnharmonicSpelling + + +@dataclass +class DrumSoundStep: + waveform: int + frequency: int + volume: int + duration: int + + +@dataclass +class DrumInstrument: + startFrequency: int + startVolume: int + steps: List[DrumSoundStep] + name: Optional[str] = None From 0c6ba1b63cfb215f8cbb7fe8c7de1551ceac0345 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 24 May 2026 23:19:09 -0400 Subject: [PATCH 03/22] Oops accidentally used lowerCamelCase instead of snake_case --- src/arcade/music.py | 388 +++++++++++++++++++------------------- src/arcade/music_types.py | 26 +-- 2 files changed, 208 insertions(+), 206 deletions(-) diff --git a/src/arcade/music.py b/src/arcade/music.py index a1d0d42..405da8a 100644 --- a/src/arcade/music.py +++ b/src/arcade/music.py @@ -8,23 +8,23 @@ logger = create_logger(name=__name__, level=logging.INFO) -def set8BitNumber(buf: bytearray, offset: int, value: int): +def set_8_bit_number(buf: bytearray, offset: int, value: int): struct.pack_into(" int: +def get_8_bit_number(buf: bytearray, offset: int) -> int: return struct.unpack_from(" int: +def get_16_bit_number(buf: bytearray, offset: int) -> int: return struct.unpack_from(" bytearray: +def encode_song(song: Song) -> bytearray: """ Encode a MakeCode Arcade song into a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L96 @@ -32,36 +32,37 @@ def encodeSong(song: Song) -> bytearray: :param song: The MakeCode Arcade song. :return: A bytearray, convert this to hex to use in a MakeCode Arcade program. """ - encodedTracks = [encodeTrack(track) for track in song.tracks if - len(track.notes) > 0] - encodedTrackVelocities: List[bytearray] = list(filter(lambda v: v is not None, - [encodeTrackVelocity(track) - for track in - song.tracks])) + encoded_tracks = [encode_track(track) for track in song.tracks if + len(track.notes) > 0] + encoded_track_velocities: List[bytearray] = list(filter(lambda v: v is not None, + [encode_track_velocity( + track) + for track in + song.tracks])) - trackLength = sum(len(c) for c in (encodedTracks + encodedTrackVelocities)) + track_length = sum(len(c) for c in (encoded_tracks + encoded_track_velocities)) - out = bytearray(7 + trackLength) - set8BitNumber(out, 0, 0) # encoding version - set16BitNumber(out, 1, song.beatsPerMinute) - set8BitNumber(out, 3, song.beatsPerMeasure) - set8BitNumber(out, 4, song.ticksPerBeat) - set8BitNumber(out, 5, song.measures) - set8BitNumber(out, 6, len(encodedTracks)) + out = bytearray(7 + track_length) + set_8_bit_number(out, 0, 0) # encoding version + set_16_bit_number(out, 1, song.beats_per_minute) + set_8_bit_number(out, 3, song.beats_per_measure) + set_8_bit_number(out, 4, song.ticks_per_beat) + set_8_bit_number(out, 5, song.measures) + set_8_bit_number(out, 6, len(encoded_tracks)) current = 7 - for track in encodedTracks: + for track in encoded_tracks: out[current:current + len(track)] = track current += len(track) - for trackVelocity in encodedTrackVelocities: + for trackVelocity in encoded_track_velocities: out[current:current + len(trackVelocity)] = trackVelocity current += len(trackVelocity) return out -def encodeInstrument(instrument: Instrument) -> bytearray: +def encode_instrument(instrument: Instrument) -> bytearray: """ Encode a MakeCode Arcade instrument into a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L127 @@ -70,41 +71,41 @@ def encodeInstrument(instrument: Instrument) -> bytearray: :return: A bytearray. """ out = bytearray(28) - set8BitNumber(out, 0, instrument.waveform) - set16BitNumber(out, 1, instrument.ampEnvelope.attack) - set16BitNumber(out, 3, instrument.ampEnvelope.decay) - set16BitNumber(out, 5, instrument.ampEnvelope.sustain) - set16BitNumber(out, 7, instrument.ampEnvelope.release) - set16BitNumber(out, 9, instrument.ampEnvelope.amplitude) - if instrument.pitchEnvelope is not None: - set16BitNumber(out, 11, instrument.pitchEnvelope.attack) - set16BitNumber(out, 13, instrument.pitchEnvelope.decay) - set16BitNumber(out, 15, instrument.pitchEnvelope.sustain) - set16BitNumber(out, 17, instrument.pitchEnvelope.release) - set16BitNumber(out, 19, instrument.pitchEnvelope.amplitude) + set_8_bit_number(out, 0, instrument.waveform) + set_16_bit_number(out, 1, instrument.amp_envelope.attack) + set_16_bit_number(out, 3, instrument.amp_envelope.decay) + set_16_bit_number(out, 5, instrument.amp_envelope.sustain) + set_16_bit_number(out, 7, instrument.amp_envelope.release) + set_16_bit_number(out, 9, instrument.amp_envelope.amplitude) + if instrument.pitch_envelope is not None: + set_16_bit_number(out, 11, instrument.pitch_envelope.attack) + set_16_bit_number(out, 13, instrument.pitch_envelope.decay) + set_16_bit_number(out, 15, instrument.pitch_envelope.sustain) + set_16_bit_number(out, 17, instrument.pitch_envelope.release) + set_16_bit_number(out, 19, instrument.pitch_envelope.amplitude) else: - set16BitNumber(out, 11, 0) - set16BitNumber(out, 13, 0) - set16BitNumber(out, 15, 0) - set16BitNumber(out, 17, 0) - set16BitNumber(out, 19, 0) - if instrument.ampLFO is not None: - set8BitNumber(out, 21, instrument.ampLFO.frequency) - set16BitNumber(out, 22, instrument.ampLFO.amplitude) + set_16_bit_number(out, 11, 0) + set_16_bit_number(out, 13, 0) + set_16_bit_number(out, 15, 0) + set_16_bit_number(out, 17, 0) + set_16_bit_number(out, 19, 0) + if instrument.amp_lfo is not None: + set_8_bit_number(out, 21, instrument.amp_lfo.frequency) + set_16_bit_number(out, 22, instrument.amp_lfo.amplitude) else: - set8BitNumber(out, 21, 0) - set16BitNumber(out, 22, 0) - if instrument.pitchLFO is not None: - set8BitNumber(out, 24, instrument.pitchLFO.frequency) - set16BitNumber(out, 25, instrument.pitchLFO.amplitude) + set_8_bit_number(out, 21, 0) + set_16_bit_number(out, 22, 0) + if instrument.pitch_lfo is not None: + set_8_bit_number(out, 24, instrument.pitch_lfo.frequency) + set_16_bit_number(out, 25, instrument.pitch_lfo.amplitude) else: - set8BitNumber(out, 24, 0) - set16BitNumber(out, 25, 0) - set8BitNumber(out, 27, instrument.octave if instrument.octave is not None else 0) + set_8_bit_number(out, 24, 0) + set_16_bit_number(out, 25, 0) + set_8_bit_number(out, 27, instrument.octave if instrument.octave is not None else 0) return out -def encodeDrumInstrument(drum: DrumInstrument) -> bytearray: +def encode_drum_instrument(drum: DrumInstrument) -> bytearray: """ Encode a MakeCode Arcade drum instrument into a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L149 @@ -113,63 +114,64 @@ def encodeDrumInstrument(drum: DrumInstrument) -> bytearray: :return: A bytearray. """ out = bytearray(5 + 7 * len(drum.steps)) - set8BitNumber(out, 0, len(drum.steps)) - set16BitNumber(out, 1, drum.startFrequency) - set16BitNumber(out, 3, drum.startVolume) + set_8_bit_number(out, 0, len(drum.steps)) + set_16_bit_number(out, 1, drum.start_frequency) + set_16_bit_number(out, 3, drum.start_volume) for i, step in enumerate(drum.steps): start = 5 + i * 7 - set8BitNumber(out, start, step.waveform) - set16BitNumber(out, start + 1, step.frequency) - set16BitNumber(out, start + 3, step.volume) - set16BitNumber(out, start + 5, step.duration) + set_8_bit_number(out, start, step.waveform) + set_16_bit_number(out, start + 1, step.frequency) + set_16_bit_number(out, start + 3, step.volume) + set_16_bit_number(out, start + 5, step.duration) return out -def encodeNoteEvent(event: NoteEvent, instrumentOctave: int, - isDrumTrack: bool) -> bytearray: +def encode_note_event(event: NoteEvent, instrument_octave: int, + is_drum_track: bool) -> bytearray: """ Encode a MakeCode Arcade note event into a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L166 :param event: The MakeCode Arcade `NoteEvent`. - :param instrumentOctave: The instrument octave offset used for the note event. - :param isDrumTrack: Whether this note event is for a drum track or not. + :param instrument_octave: The instrument octave offset used for the note event. + :param is_drum_track: Whether this note event is for a drum track or not. :return: A bytearray. """ out = bytearray(5 + len(event.notes)) - set16BitNumber(out, 0, event.startTick) - set16BitNumber(out, 2, event.endTick) - set8BitNumber(out, 4, len(event.notes)) + set_16_bit_number(out, 0, event.start_tick) + set_16_bit_number(out, 2, event.end_tick) + set_8_bit_number(out, 4, len(event.notes)) for i, note in enumerate(event.notes): - set8BitNumber(out, 5 + i, encodeNote(note, instrumentOctave, isDrumTrack)) + set_8_bit_number(out, 5 + i, + encode_note(note, instrument_octave, is_drum_track)) return out -def encodeNote(note: Note, instrumentOctave: int, isDrumTrack: bool) -> int: +def encode_note(note: Note, instrument_octave: int, is_drum_track: bool) -> int: """ Encode a MakeCode Arcade note into a single byte. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L179 :param note: The MakeCode Arcade `Note`. - :param instrumentOctave: The instrument octave offset used for the note. - :param isDrumTrack: Whether this note is for a drum track or not. + :param instrument_octave: The instrument octave offset used for the note. + :param is_drum_track: Whether this note is for a drum track or not. :return: An int, which will fit into a single byte. """ - if isDrumTrack: + if is_drum_track: return note.note flags = 0 - if note.enharmonicSpelling == EnharmonicSpelling.FLAT: + if note.enharmonic_spelling == EnharmonicSpelling.FLAT: flags = 1 - elif note.enharmonicSpelling == EnharmonicSpelling.SHARP: + elif note.enharmonic_spelling == EnharmonicSpelling.SHARP: flags = 2 - return (note.note - (instrumentOctave - 2) * 12) | (flags << 6) + return (note.note - (instrument_octave - 2) * 12) | (flags << 6) -def encodeTrack(track: Track) -> bytearray: +def encode_track(track: Track) -> bytearray: """ Encode a MakeCode Arcade track into a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L195 @@ -178,12 +180,12 @@ def encodeTrack(track: Track) -> bytearray: :return: A bytearray. """ if track.drums: - return encodeDrumTrack(track) + return encode_drum_track(track) else: - return encodeMelodicTrack(track) + return encode_melodic_track(track) -def encodeTrackVelocity(track: Track) -> Optional[bytearray]: +def encode_track_velocity(track: Track) -> Optional[bytearray]: """ Encode a MakeCode Arcade track's velocity data into a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L200 @@ -196,13 +198,13 @@ def encodeTrackVelocity(track: Track) -> Optional[bytearray]: return None out = bytearray(1 + len(track.notes)) - set8BitNumber(out, 0, track.id) + set_8_bit_number(out, 0, track.id) for i, note in enumerate(track.notes): - set8BitNumber(out, 1 + i, note.velocity if note.velocity is not None else 0) + set_8_bit_number(out, 1 + i, note.velocity if note.velocity is not None else 0) return out -def encodeMelodicTrack(track: Track) -> bytearray: +def encode_melodic_track(track: Track) -> bytearray: """ Encode a MakeCode Arcade melodic track into a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L211 @@ -210,30 +212,30 @@ def encodeMelodicTrack(track: Track) -> bytearray: :param track: The MakeCode Arcade `Track`, must be melodic. :return: A bytearray. """ - encodedInstrument = encodeInstrument(track.instrument) - encodedNotes = [encodeNoteEvent(note, track.instrument.octave, False) for note in - track.notes] - noteLength = sum(len(c) for c in encodedNotes) + encoded_instrument = encode_instrument(track.instrument) + encoded_notes = [encode_note_event(note, track.instrument.octave, False) for note in + track.notes] + note_length = sum(len(c) for c in encoded_notes) - out = bytearray(6 + len(encodedInstrument) + noteLength) - set8BitNumber(out, 0, track.id) - set8BitNumber(out, 1, 0) + out = bytearray(6 + len(encoded_instrument) + note_length) + set_8_bit_number(out, 0, track.id) + set_8_bit_number(out, 1, 0) - set16BitNumber(out, 2, len(encodedInstrument)) + set_16_bit_number(out, 2, len(encoded_instrument)) current = 4 - out[current:current + len(encodedInstrument)] = encodedInstrument - current += len(encodedInstrument) + out[current:current + len(encoded_instrument)] = encoded_instrument + current += len(encoded_instrument) - set16BitNumber(out, current, noteLength) + set_16_bit_number(out, current, note_length) current += 2 - for note in encodedNotes: + for note in encoded_notes: out[current:current + len(note)] = note current += len(note) return out -def encodeDrumTrack(track: Track) -> bytearray: +def encode_drum_track(track: Track) -> bytearray: """ Encode a MakeCode Arcade drum track into a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L235 @@ -242,32 +244,32 @@ def encodeDrumTrack(track: Track) -> bytearray: :return: A bytearray. """ assert track.drums is not None - encodedDrums = [encodeDrumInstrument(drum) for drum in track.drums] - drumLength = sum(len(c) for c in encodedDrums) + encoded_drums = [encode_drum_instrument(drum) for drum in track.drums] + drum_length = sum(len(c) for c in encoded_drums) - encodedNotes = [encodeNoteEvent(note, 0, True) for note in track.notes] - noteLength = sum(len(c) for c in encodedNotes) + encoded_notes = [encode_note_event(note, 0, True) for note in track.notes] + note_length = sum(len(c) for c in encoded_notes) - out = bytearray(6 + drumLength + noteLength) - set8BitNumber(out, 0, track.id) - set8BitNumber(out, 1, 1) - set16BitNumber(out, 2, drumLength) + out = bytearray(6 + drum_length + note_length) + set_8_bit_number(out, 0, track.id) + set_8_bit_number(out, 1, 1) + set_16_bit_number(out, 2, drum_length) current = 4 - for drum in encodedDrums: + for drum in encoded_drums: out[current:current + len(drum)] = drum current += len(drum) - set16BitNumber(out, current, noteLength) + set_16_bit_number(out, current, note_length) current += 2 - for note in encodedNotes: + for note in encoded_notes: out[current:current + len(note)] = note current += len(note) return out -def decodeSong(buf: bytearray) -> Song: +def decode_song(buf: bytearray) -> Song: """ Decode a MakeCode Arcade song from a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L269 @@ -275,27 +277,27 @@ def decodeSong(buf: bytearray) -> Song: :param buf: A bytearray of an entire song. :return: A MakeCode Arcade `Song`. """ - res = Song(beatsPerMinute=get16BitNumber(buf, 1), - beatsPerMeasure=get8BitNumber(buf, 3), - ticksPerBeat=get8BitNumber(buf, 4), - measures=get8BitNumber(buf, 5), + res = Song(beats_per_minute=get_16_bit_number(buf, 1), + beats_per_measure=get_8_bit_number(buf, 3), + ticks_per_beat=get_8_bit_number(buf, 4), + measures=get_8_bit_number(buf, 5), tracks=[]) - numTracks = get8BitNumber(buf, 6) + num_tracks = get_8_bit_number(buf, 6) current = 7 - for _ in range(numTracks): - track, pointer = decodeTrack(buf, current) + for _ in range(num_tracks): + track, pointer = decode_track(buf, current) current = pointer res.tracks.append(track) while current < len(buf): - current = decodeTrackVelocity(buf, res.tracks, current) + current = decode_track_velocity(buf, res.tracks, current) return res -def decodeInstrument(buf: bytearray, offset: int) -> Instrument: +def decode_instrument(buf: bytearray, offset: int) -> Instrument: """ Decode a MakeCode Arcade instrument from a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L294 @@ -306,36 +308,36 @@ def decodeInstrument(buf: bytearray, offset: int) -> Instrument: :return: A MakeCode Arcade `Instrument`. """ return Instrument( - waveform=get8BitNumber(buf, offset), - ampEnvelope=Envelope( - attack=get16BitNumber(buf, offset + 1), - decay=get16BitNumber(buf, offset + 3), - sustain=get16BitNumber(buf, offset + 5), - release=get16BitNumber(buf, offset + 7), - amplitude=get16BitNumber(buf, offset + 9), + waveform=get_8_bit_number(buf, offset), + amp_envelope=Envelope( + attack=get_16_bit_number(buf, offset + 1), + decay=get_16_bit_number(buf, offset + 3), + sustain=get_16_bit_number(buf, offset + 5), + release=get_16_bit_number(buf, offset + 7), + amplitude=get_16_bit_number(buf, offset + 9), ), - pitchEnvelope=Envelope( - attack=get16BitNumber(buf, offset + 11), - decay=get16BitNumber(buf, offset + 13), - sustain=get16BitNumber(buf, offset + 15), - release=get16BitNumber(buf, offset + 17), - amplitude=get16BitNumber(buf, offset + 19), + pitch_envelope=Envelope( + attack=get_16_bit_number(buf, offset + 11), + decay=get_16_bit_number(buf, offset + 13), + sustain=get_16_bit_number(buf, offset + 15), + release=get_16_bit_number(buf, offset + 17), + amplitude=get_16_bit_number(buf, offset + 19), ), - ampLFO=LFO( - frequency=get8BitNumber(buf, offset + 21), - amplitude=get16BitNumber(buf, offset + 22), + amp_lfo=LFO( + frequency=get_8_bit_number(buf, offset + 21), + amplitude=get_16_bit_number(buf, offset + 22), # the original implementation is 22 instead of offset + 22 ), - pitchLFO=LFO( - frequency=get8BitNumber(buf, offset + 24), - amplitude=get16BitNumber(buf, offset + 25), + pitch_lfo=LFO( + frequency=get_8_bit_number(buf, offset + 24), + amplitude=get_16_bit_number(buf, offset + 25), # the original implementation is 25 instead of offset + 25 ), - octave=get8BitNumber(buf, offset + 27), + octave=get_8_bit_number(buf, offset + 27), ) -def decodeTrack(buf: bytearray, offset: int) -> Tuple[Track, int]: +def decode_track(buf: bytearray, offset: int) -> Tuple[Track, int]: """ Decode a MakeCode Arcade track from a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L323 @@ -345,13 +347,13 @@ def decodeTrack(buf: bytearray, offset: int) -> Tuple[Track, int]: data from. :return: A MakeCode Arcade `Track`. """ - if get8BitNumber(buf, offset + 1) != 0: - return decodeDrumTrack(buf, offset) + if get_8_bit_number(buf, offset + 1) != 0: + return decode_drum_track(buf, offset) else: - return decodeMelodicTrack(buf, offset) + return decode_melodic_track(buf, offset) -def decodeTrackVelocity(buf: bytearray, tracks: List[Track], offset: int) -> int: +def decode_track_velocity(buf: bytearray, tracks: List[Track], offset: int) -> int: """ Decode a MakeCode Arcade track velocity from a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L331 @@ -363,16 +365,16 @@ def decodeTrackVelocity(buf: bytearray, tracks: List[Track], offset: int) -> int data from. :return: The next offset after the end of this track velocity data. """ - trackId = get8BitNumber(buf, offset) - track = next((t for t in tracks if t.id == trackId), None) + track_id = get_8_bit_number(buf, offset) + track = next((t for t in tracks if t.id == track_id), None) if track is None: - raise ValueError(f"Track with {trackId} not found") + raise ValueError(f"Track with {track_id} not found") for i in range(len(track.notes)): - track.notes[i].velocity = get8BitNumber(buf, offset + i + 1) + track.notes[i].velocity = get_8_bit_number(buf, offset + i + 1) return offset + len(track.notes) + 1 -def decodeDrumInstrument(buf: bytearray, offset: int) -> DrumInstrument: +def decode_drum_instrument(buf: bytearray, offset: int) -> DrumInstrument: """ Decode a MakeCode Arcade drum instrument from a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L341 @@ -383,25 +385,25 @@ def decodeDrumInstrument(buf: bytearray, offset: int) -> DrumInstrument: :return: A MakeCode Arcade `DrumInstrument`. """ res = DrumInstrument( - startFrequency=get16BitNumber(buf, offset + 1), - startVolume=get16BitNumber(buf, offset + 3), + start_frequency=get_16_bit_number(buf, offset + 1), + start_volume=get_16_bit_number(buf, offset + 3), steps=[], ) - for i in range(get8BitNumber(buf, offset)): + for i in range(get_8_bit_number(buf, offset)): start = offset + 5 + i * 7 res.steps.append(DrumSoundStep( - waveform=get8BitNumber(buf, start), - frequency=get16BitNumber(buf, start + 1), - volume=get16BitNumber(buf, start + 3), - duration=get16BitNumber(buf, start + 5) + waveform=get_8_bit_number(buf, start), + frequency=get_16_bit_number(buf, start + 1), + volume=get_16_bit_number(buf, start + 3), + duration=get_16_bit_number(buf, start + 5) )) return res -def decodeNoteEvent(buf: bytearray, offset: int, instrumentOctave: int, - isDrumTrack: bool) -> NoteEvent: +def decode_note_event(buf: bytearray, offset: int, instrument_octave: int, + is_drum_track: bool) -> NoteEvent: """ Decode a MakeCode Arcade note event from a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L361 @@ -409,56 +411,56 @@ def decodeNoteEvent(buf: bytearray, offset: int, instrumentOctave: int, :param buf: A bytearray of an entire song. :param offset: The offset in the bytearray which to start reading the note event data from. - :param instrumentOctave: The instrument's octave offset used for the note event, + :param instrument_octave: The instrument's octave offset used for the note event, needed to decode the note value itself. - :param isDrumTrack: Whether this note event is for a drum track or not, needed to + :param is_drum_track: Whether this note event is for a drum track or not, needed to decode the note. :return: A MakeCode Arcade `NoteEvent`. """ res = NoteEvent( - startTick=get16BitNumber(buf, offset), - endTick=get16BitNumber(buf, offset + 2), + start_tick=get_16_bit_number(buf, offset), + end_tick=get_16_bit_number(buf, offset + 2), notes=[], ) - for i in range(get8BitNumber(buf, offset + 4)): + for i in range(get_8_bit_number(buf, offset + 4)): res.notes.append( - decodeNote( - get8BitNumber(buf, offset + 5 + i), - instrumentOctave, - isDrumTrack + decode_note( + get_8_bit_number(buf, offset + 5 + i), + instrument_octave, + is_drum_track ) ) return res -def decodeNote(note: int, instrumentOctave: int, isDrumTrack: bool) -> Note: +def decode_note(note: int, instrument_octave: int, is_drum_track: bool) -> Note: """ Construct a MakeCode Arcade note from specified parameters. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L374 :param note: The note number itself, between 0 and 63. For melodic tracks, the instrument octave is taken into account. - :param instrumentOctave: The track's instrument's octave offset. - :param isDrumTrack: Whether this note is for a drum track or not. + :param instrument_octave: The track's instrument's octave offset. + :param is_drum_track: Whether this note is for a drum track or not. :return: A MakeCode Arcade `Note`. """ flags = note >> 6 res = Note( - note=note if isDrumTrack else ((note & 0x3F) + (instrumentOctave - 2) * 12), - enharmonicSpelling=EnharmonicSpelling.NORMAL + note=note if is_drum_track else ((note & 0x3F) + (instrument_octave - 2) * 12), + enharmonic_spelling=EnharmonicSpelling.NORMAL ) if flags == 1: - res.enharmonicSpelling = EnharmonicSpelling.FLAT + res.enharmonic_spelling = EnharmonicSpelling.FLAT elif flags == 2: - res.enharmonicSpelling = EnharmonicSpelling.SHARP + res.enharmonic_spelling = EnharmonicSpelling.SHARP return res -def decodeMelodicTrack(buf: bytearray, offset: int) -> Tuple[Track, int]: +def decode_melodic_track(buf: bytearray, offset: int) -> Tuple[Track, int]: """ Decode a MakeCode Arcade melodic track from a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L392 @@ -470,26 +472,26 @@ def decodeMelodicTrack(buf: bytearray, offset: int) -> Tuple[Track, int]: this track data. """ res = Track( - id=get8BitNumber(buf, offset), - instrument=decodeInstrument(buf, offset + 4), + id=get_8_bit_number(buf, offset), + instrument=decode_instrument(buf, offset + 4), notes=[] ) - noteStart = offset + 4 + get16BitNumber(buf, offset + 2) - noteLength = get16BitNumber(buf, noteStart) + note_start = offset + 4 + get_16_bit_number(buf, offset + 2) + note_length = get_16_bit_number(buf, note_start) - currentOffset = noteStart + 2 + current_offset = note_start + 2 - while currentOffset < noteStart + 2 + noteLength: + while current_offset < note_start + 2 + note_length: res.notes.append( - decodeNoteEvent(buf, currentOffset, res.instrument.octave, False) + decode_note_event(buf, current_offset, res.instrument.octave, False) ) - currentOffset += 5 + len(res.notes[-1].notes) + current_offset += 5 + len(res.notes[-1].notes) - return res, currentOffset + return res, current_offset -def decodeDrumTrack(buf: bytearray, offset: int) -> Tuple[Track, int]: +def decode_drum_track(buf: bytearray, offset: int) -> Tuple[Track, int]: """ Decode a MakeCode Arcade drum track from a bytearray. Ported from https://github.com/microsoft/pxt/blob/master/pxtlib/music.ts#L412 @@ -501,26 +503,26 @@ def decodeDrumTrack(buf: bytearray, offset: int) -> Tuple[Track, int]: this track data. """ res = Track( - id=get8BitNumber(buf, offset), + id=get_8_bit_number(buf, offset), instrument=Instrument( - ampEnvelope=Envelope(attack=0, decay=0, sustain=0, release=0, amplitude=0), + amp_envelope=Envelope(attack=0, decay=0, sustain=0, release=0, amplitude=0), waveform=0, octave=0), notes=[], drums=[] ) - drumByteLength = get16BitNumber(buf, offset + 2) - currentOffset = offset + 4 + drum_byte_length = get_16_bit_number(buf, offset + 2) + current_offset = offset + 4 - while currentOffset < (offset + 4 + drumByteLength): - res.drums.append(decodeDrumInstrument(buf, currentOffset)) - currentOffset += 5 + 7 * len(res.drums[-1].steps) + while current_offset < (offset + 4 + drum_byte_length): + res.drums.append(decode_drum_instrument(buf, current_offset)) + current_offset += 5 + 7 * len(res.drums[-1].steps) - noteLength = get16BitNumber(buf, currentOffset) - currentOffset += 2 + note_length = get_16_bit_number(buf, current_offset) + current_offset += 2 - while currentOffset < (offset + 4 + drumByteLength + noteLength): - res.notes.append(decodeNoteEvent(buf, currentOffset, 0, True)) - currentOffset += 5 + len(res.notes[-1].notes) + while current_offset < (offset + 4 + drum_byte_length + note_length): + res.notes.append(decode_note_event(buf, current_offset, 0, True)) + current_offset += 5 + len(res.notes[-1].notes) - return res, currentOffset + return res, current_offset diff --git a/src/arcade/music_types.py b/src/arcade/music_types.py index fca48f9..f9f9017 100644 --- a/src/arcade/music_types.py +++ b/src/arcade/music_types.py @@ -88,11 +88,11 @@ @dataclass class Instrument: waveform: int - ampEnvelope: Envelope + amp_envelope: Envelope octave: int - pitchEnvelope: Optional[Envelope] = None - ampLFO: Optional[LFO] = None - pitchLFO: Optional[LFO] = None + pitch_envelope: Optional[Envelope] = None + amp_lfo: Optional[LFO] = None + pitch_lfo: Optional[LFO] = None @dataclass @@ -113,9 +113,9 @@ class LFO: @dataclass class SongInfo: measures: int - beatsPerMeasure: int - beatsPerMinute: int - ticksPerBeat: int + beats_per_measure: int + beats_per_minute: int + ticks_per_beat: int @dataclass @@ -130,14 +130,14 @@ class Track: notes: List[NoteEvent] drums: Optional[List[DrumInstrument]] = None name: Optional[str] = None - iconURI: Optional[str] = None + icon_uri: Optional[str] = None @dataclass class NoteEvent: notes: List[Note] - startTick: int - endTick: int + start_tick: int + end_tick: int velocity: Optional[int] = None @@ -150,7 +150,7 @@ class EnharmonicSpelling(Enum): @dataclass class Note: note: int - enharmonicSpelling: EnharmonicSpelling + enharmonic_spelling: EnharmonicSpelling @dataclass @@ -163,7 +163,7 @@ class DrumSoundStep: @dataclass class DrumInstrument: - startFrequency: int - startVolume: int + start_frequency: int + start_volume: int steps: List[DrumSoundStep] name: Optional[str] = None From eb19c440d757b5c1a0011f15b005a1a3e668c992 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 24 May 2026 23:37:45 -0400 Subject: [PATCH 04/22] Encode and decode from bare hex buffers. --- src/arcade/music.py | 467 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 467 insertions(+) diff --git a/src/arcade/music.py b/src/arcade/music.py index 405da8a..b27b1d0 100644 --- a/src/arcade/music.py +++ b/src/arcade/music.py @@ -24,6 +24,20 @@ def get_16_bit_number(buf: bytearray, offset: int) -> int: return struct.unpack_from(" str: + """ + Encode a MakeCode Arcade song into a hex string. Surround this hex string with + hex` and ` to create a valid MakeCode Arcade TypeScript buffer for MakeCode Arcade + to use. + + :param song: The MakeCode Arcade song. + :return: A hex representation of the bytearray that is generated by + `encode_song(song)`. + """ + encoded = encode_song(song) + return "".join(f"{byte:02X}" for byte in encoded) + + def encode_song(song: Song) -> bytearray: """ Encode a MakeCode Arcade song into a bytearray. Ported from @@ -269,6 +283,19 @@ def encode_drum_track(track: Track) -> bytearray: return out +def decode_song_from_hex(hex: str) -> Song: + """ + Decode a MakeCode Arcade song from a hex string. The parts "hex`" and "`" before and + after a typical MakeCode Arcade TypeScript buffer must have been removed before + using this function. + + :param hex: The bare hex buffer of the MakeCode Arcade song. + :return: A MakeCode Arcade `Song` that is generated by `decode_song(buf)`. + """ + buf = bytearray.fromhex(hex) + return decode_song(buf) + + def decode_song(buf: bytearray) -> Song: """ Decode a MakeCode Arcade song from a bytearray. Ported from @@ -526,3 +553,443 @@ def decode_drum_track(buf: bytearray, offset: int) -> Tuple[Track, int]: current_offset += 5 + len(res.notes[-1].notes) return res, current_offset + + +def get_empty_song(measures: int) -> Song: + """ + Generate an empty MakeCode Arcade song with the specified number of measures, + including the default instruments. + + :param measures: The number of measures to include. + :return: A MakeCode Arcade `Song`. + """ + tracks: List[Track] = [ + Track( + id=0, + name="Dog", + notes=[], + icon_uri="music-editor/dog.png", + instrument=Instrument( + waveform=1, + octave=4, + amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=100, + amplitude=1024), + pitch_envelope=None, + amp_lfo=None, + pitch_lfo=LFO(frequency=5, amplitude=0) + ) + ), + Track( + id=1, + name="Duck", + notes=[], + icon_uri="music-editor/duck.png", + instrument=Instrument( + waveform=15, + octave=4, + amp_envelope=Envelope(attack=5, decay=530, sustain=705, release=450, + amplitude=1024), + pitch_envelope=Envelope(attack=5, decay=40, sustain=0, release=100, + amplitude=40), + amp_lfo=LFO(frequency=3, amplitude=20), + pitch_lfo=LFO(frequency=6, amplitude=2) + ) + ), + Track( + id=2, + name="Cat", + notes=[], + icon_uri="music-editor/cat.png", + instrument=Instrument( + waveform=12, + octave=5, + amp_envelope=Envelope(attack=150, decay=100, sustain=365, release=400, + amplitude=1024), + pitch_envelope=Envelope(attack=120, decay=300, sustain=0, release=100, + amplitude=50), + amp_lfo=None, + pitch_lfo=LFO(frequency=10, amplitude=6) + ) + ), + Track( + id=3, + name="Fish", + notes=[], + icon_uri="music-editor/fish.png", + instrument=Instrument( + waveform=1, + octave=3, + amp_envelope=Envelope(attack=220, decay=105, sustain=1024, release=350, + amplitude=1024), + amp_lfo=LFO(frequency=5, amplitude=100), + pitch_lfo=LFO(frequency=1, amplitude=4), + pitch_envelope=None + ) + ), + Track( + id=4, + name="Car", + notes=[], + icon_uri="music-editor/car.png", + instrument=Instrument( + waveform=16, + octave=4, + amp_envelope=Envelope(attack=5, decay=100, sustain=1024, release=30, + amplitude=1024), + pitch_lfo=LFO(frequency=10, amplitude=4), + pitch_envelope=None, + amp_lfo=None + ) + ), + Track( + id=5, + name="Computer", + notes=[], + icon_uri="music-editor/computer.png", + instrument=Instrument( + waveform=15, + octave=2, + amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=10, + amplitude=1024) + ) + ), + Track( + id=6, + name="Burger", + notes=[], + icon_uri="music-editor/burger.png", + instrument=Instrument( + waveform=1, + octave=2, + amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=100, + amplitude=1024) + ) + ), + Track( + id=7, + name="Cherry", + notes=[], + icon_uri="music-editor/cherry.png", + instrument=Instrument( + waveform=2, + octave=3, + amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=100, + amplitude=1024) + ) + ), + Track( + id=8, + name="Lemon", + notes=[], + icon_uri="music-editor/lemon.png", + instrument=Instrument( + waveform=14, + octave=2, + amp_envelope=Envelope(attack=5, decay=70, sustain=870, release=50, + amplitude=1024), + pitch_envelope=Envelope(attack=10, decay=45, sustain=0, release=100, + amplitude=20), + amp_lfo=LFO(frequency=1, amplitude=50), + pitch_lfo=LFO(frequency=2, amplitude=1) + ) + ), + Track( + id=9, + name="Drums", + notes=[], + icon_uri="music-editor/explosion.png", + instrument=Instrument( + waveform=11, + octave=4, + amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=100, + amplitude=1024) + ), + drums=[ + DrumInstrument( + name="neutral kick", + start_frequency=100, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=3, frequency=120, duration=10, + volume=1024), + DrumSoundStep(waveform=3, frequency=1, duration=100, volume=0), + ] + ), + DrumInstrument( + name="punchy kick", + start_frequency=200, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=1, frequency=0, duration=100, volume=0) + ] + ), + DrumInstrument( + name="booming kick", + start_frequency=100, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=1, frequency=0, duration=250, volume=0) + ] + ), + DrumInstrument( + name="snare 1", + start_frequency=175, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=1, frequency=200, duration=10, + volume=1024), + DrumSoundStep(waveform=1, frequency=150, duration=20, + volume=1024), + DrumSoundStep(waveform=5, frequency=1, duration=20, volume=100), + DrumSoundStep(waveform=5, frequency=1, duration=300, volume=0), + ] + ), + DrumInstrument( + name="snare 2", + start_frequency=220, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=1, frequency=250, duration=10, + volume=1024), + DrumSoundStep(waveform=1, frequency=200, duration=20, + volume=1024), + DrumSoundStep(waveform=5, frequency=2000, duration=20, + volume=100), + DrumSoundStep(waveform=5, frequency=2000, duration=200, + volume=0), + ] + ), + DrumInstrument( + name="hat 1", + start_frequency=400, + start_volume=500, + steps=[ + DrumSoundStep(waveform=5, frequency=450, duration=10, + volume=500), + DrumSoundStep(waveform=5, frequency=400, duration=20, + volume=20), + ] + ), + DrumInstrument( + name="hat 2", + start_frequency=400, + start_volume=0, + steps=[ + DrumSoundStep(waveform=5, frequency=450, duration=5, + volume=500), + DrumSoundStep(waveform=5, frequency=900, duration=50, volume=5), + DrumSoundStep(waveform=5, frequency=900, duration=250, + volume=0), + ] + ), + DrumInstrument( + name="hat 3", + start_frequency=400, + start_volume=0, + steps=[ + DrumSoundStep(waveform=5, frequency=450, duration=5, + volume=500), + DrumSoundStep(waveform=5, frequency=900, duration=50, + volume=200), + DrumSoundStep(waveform=5, frequency=900, duration=100, + volume=5), + DrumSoundStep(waveform=5, frequency=900, duration=400, + volume=0), + ] + ), + DrumInstrument( + name="hat 4", + start_frequency=400, + start_volume=0, + steps=[ + DrumSoundStep(waveform=5, frequency=450, duration=5, + volume=500), + DrumSoundStep(waveform=5, frequency=900, duration=100, + volume=200), + DrumSoundStep(waveform=5, frequency=900, duration=200, + volume=5), + DrumSoundStep(waveform=5, frequency=900, duration=500, + volume=0), + ] + ), + DrumInstrument( + name="double hat", + start_frequency=3500, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=4, frequency=4000, duration=10, + volume=0), + DrumSoundStep(waveform=4, frequency=3500, duration=1, + volume=800), + DrumSoundStep(waveform=4, frequency=4000, duration=40, + volume=0), + DrumSoundStep(waveform=4, frequency=3500, duration=1, + volume=400), + DrumSoundStep(waveform=4, frequency=4000, duration=40, + volume=0), + ] + ), + DrumInstrument( + name="metallic", + start_frequency=2000, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=4, frequency=1800, duration=100, + volume=15), + DrumSoundStep(waveform=4, frequency=1800, duration=200, + volume=0), + ] + ), + DrumInstrument( + name="low tom", + start_frequency=200, + start_volume=200, + steps=[ + DrumSoundStep(waveform=14, frequency=125, duration=25, + volume=200), + DrumSoundStep(waveform=14, frequency=100, duration=50, + volume=15), + DrumSoundStep(waveform=14, frequency=120, duration=250, + volume=0), + ] + ), + DrumInstrument( + name="mid tom", + start_frequency=300, + start_volume=200, + steps=[ + DrumSoundStep(waveform=14, frequency=225, duration=25, + volume=200), + DrumSoundStep(waveform=14, frequency=200, duration=50, + volume=15), + DrumSoundStep(waveform=14, frequency=220, duration=250, + volume=0), + ] + ), + DrumInstrument( + name="hi tom", + start_frequency=500, + start_volume=200, + steps=[ + DrumSoundStep(waveform=14, frequency=425, duration=25, + volume=200), + DrumSoundStep(waveform=14, frequency=400, duration=50, + volume=15), + DrumSoundStep(waveform=14, frequency=420, duration=250, + volume=0), + ] + ), + DrumInstrument( + name="lo tom 2", + start_frequency=200, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=1, frequency=75, duration=200, volume=0), + ] + ), + DrumInstrument( + name="mid tom 2", + start_frequency=300, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=1, frequency=200, duration=200, + volume=0), + ] + ), + DrumInstrument( + name="hi tom 2", + start_frequency=400, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=1, frequency=300, duration=200, + volume=0), + ] + ), + DrumInstrument( + name="thump 1", + start_frequency=200, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=4, frequency=200, duration=100, + volume=15), + DrumSoundStep(waveform=4, frequency=150, duration=200, + volume=0), + ] + ), + DrumInstrument( + name="thump 2", + start_frequency=450, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=4, frequency=350, duration=100, + volume=15), + DrumSoundStep(waveform=4, frequency=300, duration=100, + volume=0), + ] + ), + DrumInstrument( + name="cymbal", + start_frequency=2500, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=4, frequency=2500, duration=150, + volume=100), + DrumSoundStep(waveform=4, frequency=2550, duration=500, + volume=0), + ] + ), + DrumInstrument( + name="crash 1", + start_frequency=3000, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=4, frequency=3000, duration=300, + volume=100), + DrumSoundStep(waveform=4, frequency=3060, duration=500, + volume=0), + ] + ), + DrumInstrument( + name="crash 2", + start_frequency=800, + start_volume=0, + steps=[ + DrumSoundStep(waveform=4, frequency=800, duration=10, + volume=1024), + DrumSoundStep(waveform=4, frequency=800, duration=490, + volume=0), + ] + ), + DrumInstrument( + name="crash 3", + start_frequency=400, + start_volume=0, + steps=[ + DrumSoundStep(waveform=4, frequency=400, duration=10, + volume=1024), + DrumSoundStep(waveform=4, frequency=400, duration=400, + volume=0), + ] + ), + DrumInstrument( + name="buzzer", + start_frequency=2000, + start_volume=1024, + steps=[ + DrumSoundStep(waveform=16, frequency=2000, duration=150, + volume=100), + DrumSoundStep(waveform=16, frequency=2000, duration=200, + volume=0), + ] + ), + ] + ), + ] + + return Song( + measures=measures, + beats_per_measure=4, + beats_per_minute=120, + ticks_per_beat=8, + tracks=tracks + ) From 6615c0398a245e1c38677b65cd6feb44d92d5728 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Tue, 26 May 2026 00:12:28 -0400 Subject: [PATCH 05/22] Parse timeline for instrument data --- src/converter/midi_to_song.py | 279 ++++++++++++++++++++++++++++++++++ src/main.py | 26 +++- 2 files changed, 300 insertions(+), 5 deletions(-) create mode 100644 src/converter/midi_to_song.py diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py new file mode 100644 index 0000000..8634b9f --- /dev/null +++ b/src/converter/midi_to_song.py @@ -0,0 +1,279 @@ +import logging +from dataclasses import dataclass +from enum import IntEnum +from typing import List + +from mido import MidiFile, tick2second +from mido.messages.messages import Message + +from arcade.music_types import Song +from utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +@dataclass +class AbsoluteTickMessage: + tick: int # absolute MIDI tick + track: int + msg: Message + + +@dataclass +class AbsoluteTimeMessage: + time: float # absolute time in seconds + port: int + msg: Message # note_on, note_off, program_change, control_change (where control = 0) + + +def build_timeline(midi_song: MidiFile) -> List[AbsoluteTimeMessage]: + """ + Look through all tracks and convert MIDI's delta tick time to absolute time in + seconds while keeping track of tempo and port changes. + + :param midi_song: `MidiFile` object. + :return: A list of `AbsoluteTimeMessage` objects, which hold time, their original + track, and the MIDI message itself. It is sorted by time in ascending order and + filters out unneeded messages. + """ + # Gather tracks and convert to absolute time + # Unfortunately we can't simply just merge all the tracks because if channel 0 on + # track 0 was a piano and channel 0 on track 1 was a flute, then they collide + # We must keep them separate and build our own global absolute timeline afterward + # Additionally, we must keep track of tempo changes and port changes + + logger.debug("Building global absolute timeline of MIDI messages") + + # First gather all messages with their relative ticks, and convert to absolute ticks + # We need to do this in passes because some messages are globally effective while + # some others are restricted to a track only + logger.debug("Convert relative ticks to absolute ticks and sort") + all_msgs_with_abs_ticks: List[AbsoluteTickMessage] = [] + for i, track in enumerate(midi_song.tracks): + abs_tick = 0 + for msg in track: + abs_tick += msg.time + all_msgs_with_abs_ticks.append( + AbsoluteTickMessage(tick=abs_tick, track=i, msg=msg)) + # note_on and note_off should come after other messages + all_msgs_with_abs_ticks.sort( + key=lambda m: (m.tick, 0 if m.msg.type not in ("note_on", "note_off") else 1)) + + # Now we can convert absolute ticks to absolute time, but we need to keep track of + # tempo changes and MIDI port changes as well (they are also chronological) + logger.debug("Convert absolute ticks to absolute time and track tempo and port " + "changes") + global_timeline: List[AbsoluteTimeMessage] = [] + ticks_per_beat = midi_song.ticks_per_beat + msgs_skipped = 0 + + current_tempo = 500000 # the default + current_abs_time = 0.0 # in seconds (*_time is seconds) + last_abs_ticks = 0 # in MIDI ticks (*_ticks is MIDI ticks) + + track_ports = {i: 0 for i in range(len(midi_song.tracks))} + + for item in all_msgs_with_abs_ticks: + msg = item.msg + track_idx = item.track + abs_ticks = item.tick + + delta_ticks = abs_ticks - last_abs_ticks + if delta_ticks > 0: + delta_time = tick2second(delta_ticks, ticks_per_beat, current_tempo) + current_abs_time += delta_time + last_abs_ticks = abs_ticks + + if msg.type == "set_tempo": + current_tempo = msg.tempo + elif msg.type == "midi_port": + track_ports[track_idx] = msg.port + # Keep note on/off, program change, sysex, and control change (only if control + # is 0 or 32 which is the bank select MSB/LSB) + elif msg.type in ("note_on", "note_off", "program_change", "sysex") or ( + msg.type == "control_change" and msg.control in (0, 32)): + global_timeline.append( + AbsoluteTimeMessage(time=current_abs_time, port=track_ports[track_idx], + msg=msg)) + else: + msgs_skipped += 1 + logger.debug(f"Global timeline has {len(global_timeline)} messages (skipped " + f"{msgs_skipped}), total song length of {global_timeline[-1].time}s") + + return global_timeline + + +class DrumDeterminationSource(IntEnum): + DEFAULT = 0 + CC = 1 # control change + SYSEX = 2 + + +@dataclass +class ChannelState: + program: int # instrument + bank_select_msb: int + bank_select_lsb: int + is_drum: bool + drum_determined_by: DrumDeterminationSource + + +@dataclass +class AbsoluteTimeMessageWithInstrument: + time: float + port: int + instrument: int # midi instrument + is_drum: bool + msg: Message # note_on and note_off + + +def find_instrument_data_for_timeline(timeline: List[AbsoluteTimeMessage]) -> List[ + AbsoluteTimeMessageWithInstrument]: + """ + Parse the timeline for program_change and control_change (control = 0) messages to + determine what instrument each message has. + + :param timeline: A list of `AbsoluteTimeMessage` objects. + :return: A list of `AbsoluteTimeMessageWithInstrument` objects. + """ + # Read the timeline sequentially and keep track of program_change and + # control_change (control = 0) to update the current instrument for each channel on + # a port + logger.debug("Finding instrument data for each message in the timeline") + + # Initialize channel states, which keep track of the bank_select and program per + # channel and port + channel_states = {} + highest_port = 0 + try: + highest_port = max([highest_port] + [m.port for m in timeline]) + except ValueError: + pass + for port in range(highest_port + 1): + for channel in range(16): + # By default, channel 10 starts as drum + channel_states[(port, channel)] = ChannelState(program=0, + bank_select_msb=0, + bank_select_lsb=0, + is_drum=channel == 9, + drum_determined_by=DrumDeterminationSource.DEFAULT) + + # Now search through the timeline and apply control_change (control = 0) and + # program_change messages to the channel states + timeline_with_instrument: List[AbsoluteTimeMessageWithInstrument] = [] + + instr_msgs_processed = 0 + sysex_msgs_processed = 0 + + for item in timeline: + msg = item.msg + port = item.port + channel = getattr(msg, "channel", -1) + + if msg.type == "control_change": + if msg.control == 0: + channel_states[(port, channel)].bank_select_msb = msg.value + elif msg.control == 32: + channel_states[(port, channel)].bank_select_lsb = msg.value + is_drum_bank = ( + channel_states[(port, channel)].bank_select_msb in (120, 121, + 126, + 127, 128) or + channel_states[(port, channel)].bank_select_lsb in (120, 121, + 126, + 127, 128) or + channel == 9 + ) + # Only override if last drum determination was weaker than CC + if DrumDeterminationSource.CC >= channel_states[ + (port, channel)].drum_determined_by: + channel_states[(port, channel)].is_drum = is_drum_bank + channel_states[ + (port, channel)].drum_determined_by = DrumDeterminationSource.CC + instr_msgs_processed += 1 + elif msg.type == "program_change": + channel_states[(port, channel)].program = msg.program + instr_msgs_processed += 1 + elif msg.type == "sysex": + data = msg.data + # check for Roland GS + if (len(data) >= 8 and + data[0] == 0x41 and # Roland ID + data[1] == 0x10 and # device ID + data[2] == 0x42 and # GS standard layouts + data[3] == 0x12 and + data[4] == 0x40 and # parameter 1 + 0x10 <= data[5] <= 0x1F and # 0x1n, channel byte, see below + data[6] == 0x15 and # part address + data[7] in (0, 1, 2)): # map byte + # 0x1n is the channel byte, where: + # n=0 is channel 9 (midi channel 10) + # n=1 through 9 is channels 0 through 8 + # n=A through F is channels 10 through 15 + def gs_byte_to_channel(val: int) -> int: + n = val & 0x0F + if n == 0: + return 9 + if n < 10: + return n - 1 + return n + + channel = gs_byte_to_channel(data[5]) + is_drum = data[7] in (1, 2) + # Only override if last drum determination was weaker than sysex + if DrumDeterminationSource.SYSEX >= channel_states[ + (port, channel)].drum_determined_by: + channel_states[(port, channel)].is_drum = is_drum + channel_states[ + (port, + channel)].drum_determined_by = DrumDeterminationSource.SYSEX + # print(f"Roland GS channel {channel} drum: {is_drum}") + instr_msgs_processed += 1 + sysex_msgs_processed += 1 + # check for Yamaha XG + elif (len(data) >= 7 and + data[0] == 0x43 and # Yamaha ID + data[1] == 0x10 and # device ID + data[2] == 0x4C and # XG model ID + data[3] == 0x08 and # multi part params + 0x00 <= data[4] <= 0x0F and # channel, direct mapping + data[5] == 0x07 and # part address + data[6] >= 0): # map byte, technically redundant but + channel = data[4] + is_drum = data[6] > 0 + # Only override if last drum determination was weaker than sysex + if DrumDeterminationSource.SYSEX >= channel_states[ + (port, channel)].drum_determined_by: + channel_states[(port, channel)].is_drum = is_drum + channel_states[ + (port, + channel)].drum_determined_by = DrumDeterminationSource.SYSEX + # print(f"Yamaha XG channel {channel} drum: {is_drum}") + instr_msgs_processed += 1 + sysex_msgs_processed += 1 + elif msg.type in ("note_on", "note_off"): + timeline_with_instrument.append(AbsoluteTimeMessageWithInstrument( + time=item.time, + port=item.port, + instrument=channel_states[(port, channel)].program, + is_drum=channel_states[(port, channel)].is_drum, + msg=msg + )) + logger.debug(f"Global timeline has {len(timeline_with_instrument)} note messages (" + f"processed {instr_msgs_processed} instrument messages, " + f"{sysex_msgs_processed} of which were SysEx messages)") + + return timeline_with_instrument + + +def convert_midi_to_song(midi_song: MidiFile) -> Song: + """ + Convert a MIDI file into a MakeCode Arcade song. + + :param midi_song: `MidiFile` object. + :return: MakeCode Arcade `Song` object. + """ + logger.debug("Converting MIDI file into MakeCode Arcade song") + global_timeline = build_timeline(midi_song) + global_timeline = find_instrument_data_for_timeline(global_timeline) + pass diff --git a/src/main.py b/src/main.py index 9cf6afe..0b469cd 100644 --- a/src/main.py +++ b/src/main.py @@ -2,14 +2,19 @@ from argparse import ArgumentParser from pathlib import Path +from mido import MidiFile + +from arcade.music import encode_song_to_hex +from converter.midi_to_song import convert_midi_to_song from utils.logger import create_logger, set_all_stdout_logger_levels parser = ArgumentParser(description="Convert a MIDI file to a MakeCode Arcade song.") -parser.add_argument("--input", "-i", required=True, type=Path, - help="Input MIDI file") -parser.add_argument("--output", "-o", type=Path, - help="Output text file path, otherwise we will output to " - "standard output.") +parser.add_argument("--input", "-i", type=Path, + help="Input MIDI file." + # ", otherwise will read from stdin." + ) +# parser.add_argument("--output", "-o", type=Path, +# help="Output TypeScript file path, otherwise will write to stdout.") parser.add_argument("--debug", action="store_const", const=logging.DEBUG, default=logging.INFO, help="Include debug messages. Defaults to info and " @@ -18,3 +23,14 @@ logger = create_logger(name=__name__, level=logging.INFO) set_all_stdout_logger_levels(args.debug) logger.debug(f"Received arguments: {args}") + +input_path = Path(args.input) +logger.debug(f"Reading MIDI file from {input_path}") + +mid = MidiFile(input_path) +logger.debug(f"Found {len(mid.tracks)} tracks, length of {mid.length}s") + +song = convert_midi_to_song(mid) +h = encode_song_to_hex(song) +logger.info("Finished converting MIDI file") +print(f"hex`{h}`") From dfd7a19b86b65a0577393ef32850da93a330d599 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Tue, 26 May 2026 12:38:55 -0400 Subject: [PATCH 06/22] Prescan each track for the first meta midi_port to set the default --- src/converter/midi_to_song.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index 8634b9f..5c218b4 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -17,6 +17,7 @@ class AbsoluteTickMessage: tick: int # absolute MIDI tick track: int msg: Message + msg_idx: int @dataclass @@ -51,13 +52,12 @@ def build_timeline(midi_song: MidiFile) -> List[AbsoluteTimeMessage]: all_msgs_with_abs_ticks: List[AbsoluteTickMessage] = [] for i, track in enumerate(midi_song.tracks): abs_tick = 0 - for msg in track: + for j, msg in enumerate(track): abs_tick += msg.time all_msgs_with_abs_ticks.append( - AbsoluteTickMessage(tick=abs_tick, track=i, msg=msg)) - # note_on and note_off should come after other messages - all_msgs_with_abs_ticks.sort( - key=lambda m: (m.tick, 0 if m.msg.type not in ("note_on", "note_off") else 1)) + AbsoluteTickMessage(tick=abs_tick, track=i, msg=msg, msg_idx=j)) + # Update the sort, time first, then track, then order within the track + all_msgs_with_abs_ticks.sort(key=lambda m: (m.tick, m.track, m.msg_idx)) # Now we can convert absolute ticks to absolute time, but we need to keep track of # tempo changes and MIDI port changes as well (they are also chronological) @@ -72,6 +72,17 @@ def build_timeline(midi_song: MidiFile) -> List[AbsoluteTimeMessage]: last_abs_ticks = 0 # in MIDI ticks (*_ticks is MIDI ticks) track_ports = {i: 0 for i in range(len(midi_song.tracks))} + # Prescan each track for the first meta midi_port message + # Although technically we shouldn't need to do this, some notation software (notably + # MuseScore in my testing) seem to skip midi_port for the first batch of CCs and PC + # in every track, and so CCs and PCs go to port 0 while the note data goes to + # another port + # This sets up a default port that usually works + for i, track in enumerate(midi_song.tracks): + for msg in track: + if msg.type == "midi_port": + track_ports[i] = msg.port + break for item in all_msgs_with_abs_ticks: msg = item.msg @@ -144,11 +155,7 @@ def find_instrument_data_for_timeline(timeline: List[AbsoluteTimeMessage]) -> Li # Initialize channel states, which keep track of the bank_select and program per # channel and port channel_states = {} - highest_port = 0 - try: - highest_port = max([highest_port] + [m.port for m in timeline]) - except ValueError: - pass + highest_port = max([m.port for m in timeline], default=0) for port in range(highest_port + 1): for channel in range(16): # By default, channel 10 starts as drum @@ -178,10 +185,7 @@ def find_instrument_data_for_timeline(timeline: List[AbsoluteTimeMessage]) -> Li is_drum_bank = ( channel_states[(port, channel)].bank_select_msb in (120, 121, 126, - 127, 128) or - channel_states[(port, channel)].bank_select_lsb in (120, 121, - 126, - 127, 128) or + 127) or channel == 9 ) # Only override if last drum determination was weaker than CC From 0a46e02c490f64be4be3a18491b1eef37f989be7 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Tue, 26 May 2026 23:44:26 -0400 Subject: [PATCH 07/22] After prescan each track, parse for note events --- src/converter/midi_to_song.py | 103 +++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 8 deletions(-) diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index 5c218b4..593e8ec 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -1,10 +1,9 @@ import logging from dataclasses import dataclass from enum import IntEnum -from typing import List +from typing import Dict, List, Tuple -from mido import MidiFile, tick2second -from mido.messages.messages import Message +from mido import Message, MidiFile, tick2second from arcade.music_types import Song from utils.logger import create_logger @@ -27,7 +26,7 @@ class AbsoluteTimeMessage: msg: Message # note_on, note_off, program_change, control_change (where control = 0) -def build_timeline(midi_song: MidiFile) -> List[AbsoluteTimeMessage]: +def timeline_build(midi_song: MidiFile) -> List[AbsoluteTimeMessage]: """ Look through all tracks and convert MIDI's delta tick time to absolute time in seconds while keeping track of tempo and port changes. @@ -138,7 +137,7 @@ class AbsoluteTimeMessageWithInstrument: msg: Message # note_on and note_off -def find_instrument_data_for_timeline(timeline: List[AbsoluteTimeMessage]) -> List[ +def timeline_find_instrument_data(timeline: List[AbsoluteTimeMessage]) -> List[ AbsoluteTimeMessageWithInstrument]: """ Parse the timeline for program_change and control_change (control = 0) messages to @@ -265,11 +264,98 @@ def gs_byte_to_channel(val: int) -> int: )) logger.debug(f"Global timeline has {len(timeline_with_instrument)} note messages (" f"processed {instr_msgs_processed} instrument messages, " - f"{sysex_msgs_processed} of which were SysEx messages)") + f"{sysex_msgs_processed} of which were recognized SysEx messages)") return timeline_with_instrument +@dataclass +class AbsoluteCompleteNote: + start_time: float + end_time: float + + note: int + velocity: int + + instrument: int + is_drum: bool + + +def timeline_group_messages(timeline: List[AbsoluteTimeMessageWithInstrument]) -> List[ + AbsoluteCompleteNote]: + """ + Parse the timeline for note_on and note_off messages to determine the start and end + times of each note. + + :param timeline: A list of `AbsoluteTimeMessageWithInstrument` objects. + :return: A list of `AbsoluteCompleteNote` objects. + """ + # Find all note_on and note_on (velocity = 0) and note_off messages, and pair them + # up + logger.debug("Grouping messages into complete notes in the timeline") + + timeline_with_complete_notes = [] + highest_port = max([0] + [m.port for m in timeline]) + active_notes: Dict[Tuple[int, int], List[AbsoluteCompleteNote]] = { + (port, channel): [] + for port in range(highest_port + 1) + for channel in range(16) + } + last_time = 0 + + current_poly = 0 + max_poly = 0 + + for item in timeline: + msg = item.msg + port_and_channel = (item.port, msg.channel) + last_time = max(last_time, item.time) + + if msg.type == "note_on" and msg.velocity > 0: + # add to the list of playing notes + active_notes[port_and_channel].append( + AbsoluteCompleteNote( + start_time=item.time, + end_time=item.time, # will be updated when note_off found + note=msg.note, + velocity=msg.velocity, + instrument=item.instrument, + is_drum=item.is_drum, + ) + ) + current_poly += 1 + elif msg.type == "note_off" or (msg.type == "note_on" and msg.velocity == 0): + # find the playing note and finish it + for playing_note in active_notes[port_and_channel]: + if playing_note.note == msg.note: + playing_note.end_time = item.time + timeline_with_complete_notes.append(playing_note) + active_notes[port_and_channel].remove(playing_note) + current_poly -= 1 + break + else: + logger.warning(f"Could not find start of note for {item}") + max_poly = max(max_poly, current_poly) + + # handle hanging notes + hanging_count = 0 + for group_key in active_notes: + for playing_note in active_notes[group_key]: + playing_note.end_time = last_time + timeline_with_complete_notes.append(playing_note) + hanging_count += 1 + # no need to remove we're cleaning up + + # sort by start instead of when they ended + timeline_with_complete_notes.sort(key=lambda m: m.start_time) + + logger.debug(f"Global timeline has {len(timeline_with_complete_notes)} note events " + f"(maximum polyphony across all channels and ports was {max_poly} " + f"notes and had to clean up {hanging_count} hanging notes)") + + return timeline_with_complete_notes + + def convert_midi_to_song(midi_song: MidiFile) -> Song: """ Convert a MIDI file into a MakeCode Arcade song. @@ -278,6 +364,7 @@ def convert_midi_to_song(midi_song: MidiFile) -> Song: :return: MakeCode Arcade `Song` object. """ logger.debug("Converting MIDI file into MakeCode Arcade song") - global_timeline = build_timeline(midi_song) - global_timeline = find_instrument_data_for_timeline(global_timeline) + global_timeline = timeline_build(midi_song) + global_timeline = timeline_find_instrument_data(global_timeline) + global_timeline = timeline_group_messages(global_timeline) pass From e371a85dae0370b538bbb3aa77ee9cd2b189ce09 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Thu, 28 May 2026 23:11:48 -0400 Subject: [PATCH 08/22] Search for instruments used in song, create and load instrument parameter mapping file examples --- example/instrument_params.yaml | 1277 ++++++++++++++++++++++++++++++ example/instrument_params_2.yaml | 1252 +++++++++++++++++++++++++++++ requirements.txt | 1 + src/arcade/music_types.py | 2 +- src/converter/instruments.py | 131 +++ src/converter/midi_to_song.py | 48 +- src/main.py | 16 +- 7 files changed, 2717 insertions(+), 10 deletions(-) create mode 100644 example/instrument_params.yaml create mode 100644 example/instrument_params_2.yaml create mode 100644 src/converter/instruments.py diff --git a/example/instrument_params.yaml b/example/instrument_params.yaml new file mode 100644 index 0000000..74d6e30 --- /dev/null +++ b/example/instrument_params.yaml @@ -0,0 +1,1277 @@ +_comment: " +This file was generated by Gemini for testing purposes. + +Units: + Attack, decay, release: milliseconds + Sustain and amplitudes: range 0-1024 + LFO frequencies: hz + +Waveforms map to as expected by MakeCode Arcade: + triangle = 1 + sawtooth = 2 + sine = 3 + noise_tunable = 4 + noise = 5 + square_10 = 11 + square_20 = 12 + square_30 = 13 + square_40 = 14 + square_50 (or square) = 15 + cycle_16 = 16 + cycle_32 = 17 + cycle_64 (or noise_tunable_2) = 18 + +https://arcade.makecode.com/developer/sound +> The noise is an actual white noise. It ignores the requested frequency or +> note. +> +> The tunable noise tone contains a pseudorandom sample. At high frequency it +> sounds similar to white noise, at low frequency it produces irregular +> crackling noises. +> +> The cycle 16, cycle 32, and cycle 64 waveforms are repeating pseudorandom +> patterns with a cycle length of 16/32/64 samples respectively. They sound +> like a noise-distorted square wave, with short cycles being more regular and +> long cycles being noisier. +" +melodic_instruments: + - instrument: 1 + _comment: Acoustic Grand Piano + waveform: triangle + amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 2 + _comment: Bright Acoustic Piano + waveform: triangle + amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 3 + _comment: Electric Grand Piano + waveform: triangle + amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 4 + _comment: Honky-tonk Piano + waveform: sawtooth + amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 5 + _comment: Electric Piano 1 + waveform: triangle + amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 6 + _comment: Electric Piano 2 + waveform: triangle + amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 7 + _comment: Harpsichord + waveform: square_20 + amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 8 + _comment: Clavi + waveform: sawtooth + amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 9 + _comment: Celesta + waveform: sine + amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 10 + _comment: Glockenspiel + waveform: sine + amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 11 + _comment: Music Box + waveform: sine + amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 12 + _comment: Vibraphone + waveform: sine + amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 13 + _comment: Marimba + waveform: triangle + amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 14 + _comment: Xylophone + waveform: triangle + amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 15 + _comment: Tubular Bells + waveform: triangle + amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 16 + _comment: Dulcimer + waveform: triangle + amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 17 + _comment: Drawbar Organ + waveform: square_50 + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 6, amplitude: 15 } + - instrument: 18 + _comment: Percussive Organ + waveform: square_50 + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 19 + _comment: Rock Organ + waveform: square_50 + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 6, amplitude: 15 } + - instrument: 20 + _comment: Church Organ + waveform: square_50 + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 21 + _comment: Reed Organ + waveform: sawtooth + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 22 + _comment: Accordion + waveform: sawtooth + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 23 + _comment: Harmonica + waveform: sawtooth + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 24 + _comment: Tango Accordion + waveform: sawtooth + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 25 + _comment: Acoustic Guitar (nylon) + waveform: triangle + amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 26 + _comment: Acoustic Guitar (steel) + waveform: triangle + amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 27 + _comment: Electric Guitar (jazz) + waveform: triangle + amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 28 + _comment: Electric Guitar (clean) + waveform: triangle + amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 29 + _comment: Electric Guitar (muted) + waveform: square_30 + amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 30 + _comment: Overdriven Guitar + waveform: sawtooth + amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 31 + _comment: Distortion Guitar + waveform: sawtooth + amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 32 + _comment: Guitar harmonics + waveform: triangle + amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 33 + _comment: Acoustic Bass + waveform: triangle + amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 34 + _comment: Electric Bass (finger) + waveform: triangle + amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 35 + _comment: Electric Bass (pick) + waveform: triangle + amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 36 + _comment: Fretless Bass + waveform: triangle + amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 37 + _comment: Slap Bass 1 + waveform: square_10 + amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 38 + _comment: Slap Bass 2 + waveform: square_10 + amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 39 + _comment: Synth Bass 1 + waveform: square_10 + amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 40 + _comment: Synth Bass 2 + waveform: square_10 + amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 41 + _comment: Violin + waveform: sawtooth + amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 20 } + - instrument: 42 + _comment: Viola + waveform: sawtooth + amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 20 } + - instrument: 43 + _comment: Cello + waveform: sawtooth + amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 20 } + - instrument: 44 + _comment: Contrabass + waveform: sawtooth + amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 20 } + - instrument: 45 + _comment: Tremolo Strings + waveform: sawtooth + amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 20 } + - instrument: 46 + _comment: Pizzicato Strings + waveform: triangle + amp_envelope: { attack: 2, decay: 100, sustain: 0, release: 50, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 47 + _comment: Orchestral Harp + waveform: sawtooth + amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 20 } + - instrument: 48 + _comment: Timpani + waveform: triangle + amp_envelope: { attack: 5, decay: 400, sustain: 0, release: 300, amplitude: 1000 } + pitch_envelope: { attack: 0, decay: 150, sustain: 0, release: 0, amplitude: -50 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 49 + _comment: String Ensemble 1 + waveform: sawtooth + amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 15 } + - instrument: 50 + _comment: String Ensemble 2 + waveform: sawtooth + amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 15 } + - instrument: 51 + _comment: SynthStrings 1 + waveform: sawtooth + amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 15 } + - instrument: 52 + _comment: SynthStrings 2 + waveform: sawtooth + amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 15 } + - instrument: 53 + _comment: Choir Aahs + waveform: sawtooth + amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 15 } + - instrument: 54 + _comment: Voice Oohs + waveform: sawtooth + amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 15 } + - instrument: 55 + _comment: Synth Voice + waveform: sawtooth + amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 15 } + - instrument: 56 + _comment: Orchestra Hit + waveform: sawtooth + amp_envelope: { attack: 5, decay: 150, sustain: 100, release: 100, amplitude: 1024 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 57 + _comment: Trumpet + waveform: sawtooth + amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 58 + _comment: Trombone + waveform: sawtooth + amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 59 + _comment: Tuba + waveform: sawtooth + amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 60 + _comment: Muted Trumpet + waveform: square_40 + amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 61 + _comment: French Horn + waveform: sawtooth + amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 62 + _comment: Brass Section + waveform: sawtooth + amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 63 + _comment: SynthBrass 1 + waveform: square_40 + amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 64 + _comment: SynthBrass 2 + waveform: square_40 + amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 65 + _comment: Soprano Sax + waveform: square_50 + amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 12 } + - instrument: 66 + _comment: Alto Sax + waveform: square_50 + amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 12 } + - instrument: 67 + _comment: Tenor Sax + waveform: square_50 + amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 12 } + - instrument: 68 + _comment: Baritone Sax + waveform: square_50 + amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 12 } + - instrument: 69 + _comment: Oboe + waveform: square_50 + amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 12 } + - instrument: 70 + _comment: English Horn + waveform: square_50 + amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 12 } + - instrument: 71 + _comment: Bassoon + waveform: square_50 + amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 12 } + - instrument: 72 + _comment: Clarinet + waveform: square_50 + amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 12 } + - instrument: 73 + _comment: Piccolo + waveform: sine + amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 74 + _comment: Flute + waveform: sine + amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 75 + _comment: Recorder + waveform: sine + amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 76 + _comment: Pan Flute + waveform: triangle + amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 77 + _comment: Blown Bottle + waveform: triangle + amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 78 + _comment: Shakuhachi + waveform: triangle + amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 79 + _comment: Whistle + waveform: triangle + amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 80 + _comment: Ocarina + waveform: triangle + amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 81 + _comment: Lead 1 (square) + waveform: square_50 + amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 82 + _comment: Lead 2 (sawtooth) + waveform: sawtooth + amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 83 + _comment: Lead 3 (calliope) + waveform: square_50 + amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 84 + _comment: Lead 4 (chiff) + waveform: sawtooth + amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 85 + _comment: Lead 5 (charang) + waveform: sawtooth + amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 86 + _comment: Lead 6 (voice) + waveform: sawtooth + amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 87 + _comment: Lead 7 (fifths) + waveform: sawtooth + amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 88 + _comment: Lead 8 (bass + lead) + waveform: sawtooth + amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 89 + _comment: Pad 1 (new age) + waveform: triangle + amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 90 + _comment: Pad 2 (warm) + waveform: sine + amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 91 + _comment: Pad 3 (polysynth) + waveform: triangle + amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 92 + _comment: Pad 4 (choir) + waveform: sine + amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 93 + _comment: Pad 5 (bowed) + waveform: triangle + amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 94 + _comment: Pad 6 (metallic) + waveform: triangle + amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 95 + _comment: Pad 7 (halo) + waveform: triangle + amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 96 + _comment: Pad 8 (sweep) + waveform: triangle + amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 97 + _comment: FX 1 (rain) + waveform: cycle_32 + amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 98 + _comment: FX 2 (soundtrack) + waveform: sawtooth + amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 99 + _comment: FX 3 (crystal) + waveform: sawtooth + amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 100 + _comment: FX 4 (atmosphere) + waveform: sawtooth + amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 101 + _comment: FX 5 (brightness) + waveform: sawtooth + amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 102 + _comment: FX 6 (goblins) + waveform: cycle_32 + amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 103 + _comment: FX 7 (echoes) + waveform: sawtooth + amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 104 + _comment: FX 8 (sci-fi) + waveform: sawtooth + amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 105 + _comment: Sitar + waveform: sawtooth + amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 106 + _comment: Banjo + waveform: triangle + amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 107 + _comment: Shamisen + waveform: triangle + amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 108 + _comment: Koto + waveform: triangle + amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 109 + _comment: Kalimba + waveform: triangle + amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 110 + _comment: Bagpipe + waveform: sawtooth + amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 111 + _comment: Fiddle + waveform: sawtooth + amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 112 + _comment: Shanai + waveform: triangle + amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 113 + _comment: Tinkle Bell + waveform: sine + amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 114 + _comment: Agogo + waveform: triangle + amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 115 + _comment: Steel Drums + waveform: sine + amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 116 + _comment: Woodblock + waveform: triangle + amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 117 + _comment: Taiko Drum + waveform: cycle_16 + amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 118 + _comment: Melodic Tom + waveform: cycle_16 + amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 119 + _comment: Synth Drum + waveform: triangle + amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 120 + _comment: Reverse Cymbal + waveform: triangle + amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 121 + _comment: Guitar Fret Noise + waveform: noise_tunable + amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 122 + _comment: Breath Noise + waveform: noise_tunable + amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 123 + _comment: Seashore + waveform: noise + amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 124 + _comment: Bird Tweet + waveform: noise_tunable + amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 125 + _comment: Telephone Ring + waveform: noise_tunable + amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 126 + _comment: Helicopter + waveform: noise + amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 127 + _comment: Applause + waveform: noise + amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 128 + _comment: Gunshot + waveform: noise + amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } +drum_instruments: + - note: 35 + _comment: Acoustic Bass Drum + start_freq: 150 + start_vol: 1024 + steps: + - { waveform: sine, target_freq: 80, target_vol: 900, duration: 30 } + - { waveform: sine, target_freq: 50, target_vol: 400, duration: 40 } + - { waveform: sine, target_freq: 40, target_vol: 0, duration: 60 } + - note: 36 + _comment: Bass Drum 1 + start_freq: 150 + start_vol: 1024 + steps: + - { waveform: sine, target_freq: 80, target_vol: 900, duration: 30 } + - { waveform: sine, target_freq: 50, target_vol: 400, duration: 40 } + - { waveform: sine, target_freq: 40, target_vol: 0, duration: 60 } + - note: 37 + _comment: Side Stick + start_freq: 600 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 600, target_vol: 400, duration: 15 } + - { waveform: triangle, target_freq: 500, target_vol: 0, duration: 20 } + - note: 38 + _comment: Acoustic Snare + start_freq: 300 + start_vol: 1024 + steps: + - { waveform: noise, target_freq: 300, target_vol: 800, duration: 20 } + - { waveform: noise, target_freq: 200, target_vol: 400, duration: 50 } + - { waveform: noise, target_freq: 100, target_vol: 0, duration: 80 } + - note: 39 + _comment: Hand Clap + start_freq: 1000 + start_vol: 1024 + steps: + - { waveform: noise, target_freq: 1000, target_vol: 0, duration: 10 } + - { waveform: noise, target_freq: 1000, target_vol: 800, duration: 15 } + - { waveform: noise, target_freq: 800, target_vol: 0, duration: 15 } + - { waveform: noise, target_freq: 900, target_vol: 700, duration: 20 } + - { waveform: noise, target_freq: 600, target_vol: 0, duration: 60 } + - note: 40 + _comment: Electric Snare + start_freq: 300 + start_vol: 1024 + steps: + - { waveform: noise, target_freq: 300, target_vol: 800, duration: 20 } + - { waveform: noise, target_freq: 200, target_vol: 400, duration: 50 } + - { waveform: noise, target_freq: 100, target_vol: 0, duration: 80 } + - note: 41 + _comment: Low Floor Tom + start_freq: 120 + start_vol: 950 + steps: + - { waveform: sine, target_freq: 84, target_vol: 600, duration: 40 } + - { waveform: sine, target_freq: 60, target_vol: 0, duration: 80 } + - note: 42 + _comment: Closed Hi Hat + start_freq: 8000 + start_vol: 800 + steps: + - { waveform: noise, target_freq: 6000, target_vol: 300, duration: 12 } + - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 28 } + - note: 43 + _comment: High Floor Tom + start_freq: 120 + start_vol: 950 + steps: + - { waveform: sine, target_freq: 84, target_vol: 600, duration: 40 } + - { waveform: sine, target_freq: 60, target_vol: 0, duration: 80 } + - note: 44 + _comment: Pedal Hi-Hat + start_freq: 8000 + start_vol: 800 + steps: + - { waveform: noise, target_freq: 6000, target_vol: 300, duration: 12 } + - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 28 } + - note: 45 + _comment: Low Tom + start_freq: 180 + start_vol: 950 + steps: + - { waveform: sine, target_freq: 126, target_vol: 600, duration: 40 } + - { waveform: sine, target_freq: 90, target_vol: 0, duration: 80 } + - note: 46 + _comment: Open Hi-Hat + start_freq: 8000 + start_vol: 800 + steps: + - { waveform: noise, target_freq: 6000, target_vol: 300, duration: 45 } + - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 105 } + - note: 47 + _comment: Low-Mid Tom + start_freq: 180 + start_vol: 950 + steps: + - { waveform: sine, target_freq: 126, target_vol: 600, duration: 40 } + - { waveform: sine, target_freq: 90, target_vol: 0, duration: 80 } + - note: 48 + _comment: Hi-Mid Tom + start_freq: 250 + start_vol: 950 + steps: + - { waveform: sine, target_freq: 175, target_vol: 600, duration: 40 } + - { waveform: sine, target_freq: 125, target_vol: 0, duration: 80 } + - note: 49 + _comment: Crash Cymbal 1 + start_freq: 5000 + start_vol: 850 + steps: + - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } + - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } + - note: 50 + _comment: High Tom + start_freq: 250 + start_vol: 950 + steps: + - { waveform: sine, target_freq: 175, target_vol: 600, duration: 40 } + - { waveform: sine, target_freq: 125, target_vol: 0, duration: 80 } + - note: 51 + _comment: Ride Cymbal 1 + start_freq: 5000 + start_vol: 850 + steps: + - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } + - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } + - note: 52 + _comment: Chinese Cymbal + start_freq: 5000 + start_vol: 850 + steps: + - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } + - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } + - note: 53 + _comment: Ride Bell + start_freq: 5000 + start_vol: 850 + steps: + - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } + - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } + - note: 54 + _comment: Tambourine + start_freq: 6000 + start_vol: 750 + steps: + - { waveform: noise, target_freq: 5000, target_vol: 200, duration: 25 } + - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 35 } + - note: 55 + _comment: Splash Cymbal + start_freq: 5000 + start_vol: 850 + steps: + - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } + - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } + - note: 56 + _comment: Cowbell + start_freq: 540 + start_vol: 900 + steps: + - { waveform: square_40, target_freq: 540, target_vol: 600, duration: 30 } + - { waveform: square_40, target_freq: 540, target_vol: 0, duration: 120 } + - note: 57 + _comment: Crash Cymbal 2 + start_freq: 5000 + start_vol: 850 + steps: + - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } + - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } + - note: 58 + _comment: Vibraslap + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 59 + _comment: Ride Cymbal 2 + start_freq: 5000 + start_vol: 850 + steps: + - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } + - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } + - note: 60 + _comment: Hi Bongo + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 61 + _comment: Low Bongo + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 62 + _comment: Mute Hi Conga + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 63 + _comment: Open Hi Conga + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 64 + _comment: Low Conga + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 65 + _comment: High Timbale + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 66 + _comment: Low Timbale + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 67 + _comment: High Agogo + start_freq: 800 + start_vol: 900 + steps: + - { waveform: square_40, target_freq: 800, target_vol: 600, duration: 30 } + - { waveform: square_40, target_freq: 800, target_vol: 0, duration: 120 } + - note: 68 + _comment: Low Agogo + start_freq: 600 + start_vol: 900 + steps: + - { waveform: square_40, target_freq: 600, target_vol: 600, duration: 30 } + - { waveform: square_40, target_freq: 600, target_vol: 0, duration: 120 } + - note: 69 + _comment: Cabasa + start_freq: 6000 + start_vol: 750 + steps: + - { waveform: noise, target_freq: 5000, target_vol: 200, duration: 25 } + - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 35 } + - note: 70 + _comment: Maracas + start_freq: 6000 + start_vol: 750 + steps: + - { waveform: noise, target_freq: 5000, target_vol: 200, duration: 25 } + - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 35 } + - note: 71 + _comment: Short Whistle + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 72 + _comment: Long Whistle + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 73 + _comment: Short Guiro + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 74 + _comment: Long Guiro + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 75 + _comment: Claves + start_freq: 500 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 500, target_vol: 400, duration: 15 } + - { waveform: triangle, target_freq: 400, target_vol: 0, duration: 20 } + - note: 76 + _comment: Hi Wood Block + start_freq: 800 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 800, target_vol: 400, duration: 15 } + - { waveform: triangle, target_freq: 700, target_vol: 0, duration: 20 } + - note: 77 + _comment: Low Wood Block + start_freq: 500 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 500, target_vol: 400, duration: 15 } + - { waveform: triangle, target_freq: 400, target_vol: 0, duration: 20 } + - note: 78 + _comment: Mute Cuica + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 79 + _comment: Open Cuica + start_freq: 300 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 80 + _comment: Mute Triangle + start_freq: 3000 + start_vol: 700 + steps: + - { waveform: sine, target_freq: 3000, target_vol: 400, duration: 15 } + - { waveform: sine, target_freq: 2950, target_vol: 0, duration: 35 } + - note: 81 + _comment: Open Triangle + start_freq: 3000 + start_vol: 700 + steps: + - { waveform: sine, target_freq: 3000, target_vol: 400, duration: 75 } + - { waveform: sine, target_freq: 2950, target_vol: 0, duration: 175 } diff --git a/example/instrument_params_2.yaml b/example/instrument_params_2.yaml new file mode 100644 index 0000000..2979c55 --- /dev/null +++ b/example/instrument_params_2.yaml @@ -0,0 +1,1252 @@ +_comment: " +This file was generated by Claude for testing purposes. + +Units: + Attack, decay, release: milliseconds + Sustain and amplitudes: range 0-1024 + LFO frequencies: hz + +Waveforms map to as expected by MakeCode Arcade: + triangle = 1 + sawtooth = 2 + sine = 3 + noise_tunable = 4 + noise = 5 + square_10 = 11 + square_20 = 12 + square_30 = 13 + square_40 = 14 + square_50 (or square) = 15 + cycle_16 = 16 + cycle_32 = 17 + cycle_64 (or noise_tunable_2) = 18 + +https://arcade.makecode.com/developer/sound +> The noise is an actual white noise. It ignores the requested frequency or +> note. +> +> The tunable noise tone contains a pseudorandom sample. At high frequency it +> sounds similar to white noise, at low frequency it produces irregular +> crackling noises. +> +> The cycle 16, cycle 32, and cycle 64 waveforms are repeating pseudorandom +> patterns with a cycle length of 16/32/64 samples respectively. They sound +> like a noise-distorted square wave, with short cycles being more regular and +> long cycles being noisier. +" +_comment2: 'MakeCode Arcade GM instrument parameter map. All envelope times in ms. sustain/amplitude values 0-1024. LFO frequency + in Hz. Waveforms: triangle=1 sawtooth=2 sine=3 noise_tunable=4 noise=5 square_10=11 square_20=12 square_30=13 square_40=14 + square_50=15 cycle_16=16 cycle_32=17 cycle_64=18. noise ignores pitch. noise_tunable is pitch-dependent (high=white noise, + low=crackling). cycle_N are periodic pseudo-random patterns (shorter = more regular).' +melodic_instruments: + - instrument: 1 + _comment: Acoustic Grand Piano + waveform: triangle + amp_envelope: { attack: 5, decay: 400, sustain: 300, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 2 + _comment: Bright Acoustic Piano + waveform: triangle + amp_envelope: { attack: 5, decay: 280, sustain: 250, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 3 + _comment: Electric Grand Piano + waveform: triangle + amp_envelope: { attack: 5, decay: 350, sustain: 350, release: 180, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 4 + _comment: Honky-tonk Piano + waveform: triangle + amp_envelope: { attack: 5, decay: 250, sustain: 250, release: 100, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 6 } + - instrument: 5 + _comment: Electric Piano 1 + waveform: triangle + amp_envelope: { attack: 8, decay: 500, sustain: 300, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 6 + _comment: Electric Piano 2 + waveform: sine + amp_envelope: { attack: 5, decay: 400, sustain: 400, release: 200, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 7 + _comment: Harpsichord + waveform: sawtooth + amp_envelope: { attack: 2, decay: 400, sustain: 0, release: 50, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 8 + _comment: Clavinet + waveform: sawtooth + amp_envelope: { attack: 2, decay: 180, sustain: 0, release: 60, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 9 + _comment: Celesta + waveform: sine + amp_envelope: { attack: 5, decay: 900, sustain: 0, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 10 + _comment: Glockenspiel + waveform: sine + amp_envelope: { attack: 2, decay: 1100, sustain: 0, release: 300, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 11 + _comment: Music Box + waveform: sine + amp_envelope: { attack: 2, decay: 700, sustain: 0, release: 150, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 12 + _comment: Vibraphone + waveform: sine + amp_envelope: { attack: 10, decay: 900, sustain: 50, release: 500, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 5, amplitude: 50 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 13 + _comment: Marimba + waveform: triangle + amp_envelope: { attack: 2, decay: 350, sustain: 0, release: 80, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 14 + _comment: Xylophone + waveform: triangle + amp_envelope: { attack: 2, decay: 280, sustain: 0, release: 70, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 15 + _comment: Tubular Bells + waveform: sine + amp_envelope: { attack: 5, decay: 1400, sustain: 30, release: 500, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 16 + _comment: Dulcimer + waveform: triangle + amp_envelope: { attack: 5, decay: 500, sustain: 80, release: 180, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 17 + _comment: Drawbar Organ + waveform: square_50 + amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 3 } + - instrument: 18 + _comment: Percussive Organ + waveform: square_50 + amp_envelope: { attack: 5, decay: 180, sustain: 700, release: 40, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 19 + _comment: Rock Organ + waveform: square_50 + amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 20 + _comment: Church Organ + waveform: square_50 + amp_envelope: { attack: 30, decay: 0, sustain: 900, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 21 + _comment: Reed Organ + waveform: square_30 + amp_envelope: { attack: 20, decay: 0, sustain: 850, release: 80, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 22 + _comment: Accordion + waveform: square_30 + amp_envelope: { attack: 15, decay: 0, sustain: 850, release: 60, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 5, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 23 + _comment: Harmonica + waveform: square_20 + amp_envelope: { attack: 15, decay: 50, sustain: 800, release: 80, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 5, amplitude: 40 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 24 + _comment: Tango Accordion + waveform: square_30 + amp_envelope: { attack: 10, decay: 50, sustain: 850, release: 60, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 6, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 25 + _comment: Acoustic Guitar (nylon) + waveform: triangle + amp_envelope: { attack: 5, decay: 380, sustain: 80, release: 280, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 26 + _comment: Acoustic Guitar (steel) + waveform: triangle + amp_envelope: { attack: 3, decay: 320, sustain: 120, release: 220, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 27 + _comment: Electric Guitar (jazz) + waveform: triangle + amp_envelope: { attack: 5, decay: 380, sustain: 300, release: 180, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 28 + _comment: Electric Guitar (clean) + waveform: triangle + amp_envelope: { attack: 3, decay: 280, sustain: 400, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 29 + _comment: Electric Guitar (muted) + waveform: triangle + amp_envelope: { attack: 2, decay: 120, sustain: 0, release: 60, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 30 + _comment: Overdriven Guitar + waveform: cycle_16 + amp_envelope: { attack: 3, decay: 180, sustain: 700, release: 120, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 31 + _comment: Distortion Guitar + waveform: cycle_32 + amp_envelope: { attack: 2, decay: 100, sustain: 800, release: 100, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 32 + _comment: Guitar Harmonics + waveform: sine + amp_envelope: { attack: 5, decay: 700, sustain: 150, release: 300, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 33 + _comment: Acoustic Bass + waveform: triangle + amp_envelope: { attack: 5, decay: 280, sustain: 450, release: 130, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 34 + _comment: Electric Bass (finger) + waveform: triangle + amp_envelope: { attack: 5, decay: 230, sustain: 520, release: 120, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 35 + _comment: Electric Bass (pick) + waveform: sawtooth + amp_envelope: { attack: 2, decay: 180, sustain: 520, release: 90, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 36 + _comment: Fretless Bass + waveform: triangle + amp_envelope: { attack: 10, decay: 200, sustain: 600, release: 200, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 3, amplitude: 6 } + - instrument: 37 + _comment: Slap Bass 1 + waveform: triangle + amp_envelope: { attack: 2, decay: 140, sustain: 300, release: 90, amplitude: 940 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 38 + _comment: Slap Bass 2 + waveform: sawtooth + amp_envelope: { attack: 2, decay: 140, sustain: 350, release: 90, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 39 + _comment: Synth Bass 1 + waveform: square_50 + amp_envelope: { attack: 5, decay: 180, sustain: 620, release: 90, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 40 + _comment: Synth Bass 2 + waveform: sawtooth + amp_envelope: { attack: 5, decay: 180, sustain: 620, release: 90, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 41 + _comment: Violin + waveform: sawtooth + amp_envelope: { attack: 50, decay: 0, sustain: 900, release: 150, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 9 } + - instrument: 42 + _comment: Viola + waveform: sawtooth + amp_envelope: { attack: 60, decay: 0, sustain: 880, release: 150, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 8 } + - instrument: 43 + _comment: Cello + waveform: sawtooth + amp_envelope: { attack: 70, decay: 0, sustain: 880, release: 200, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 8 } + - instrument: 44 + _comment: Contrabass + waveform: sawtooth + amp_envelope: { attack: 80, decay: 0, sustain: 870, release: 200, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 3, amplitude: 7 } + - instrument: 45 + _comment: Tremolo Strings + waveform: sawtooth + amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 150, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 8, amplitude: 100 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 46 + _comment: Pizzicato Strings + waveform: triangle + amp_envelope: { attack: 3, decay: 380, sustain: 0, release: 130, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 47 + _comment: Orchestral Harp + waveform: triangle + amp_envelope: { attack: 5, decay: 480, sustain: 80, release: 280, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 48 + _comment: Timpani + waveform: triangle + amp_envelope: { attack: 3, decay: 600, sustain: 40, release: 280, amplitude: 900 } + pitch_envelope: { attack: 5, decay: 300, sustain: 0, release: 150, amplitude: 120 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 49 + _comment: String Ensemble 1 + waveform: sawtooth + amp_envelope: { attack: 80, decay: 0, sustain: 880, release: 200, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 10 } + - instrument: 50 + _comment: String Ensemble 2 + waveform: sawtooth + amp_envelope: { attack: 100, decay: 0, sustain: 860, release: 250, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 8 } + - instrument: 51 + _comment: Synth Strings 1 + waveform: sawtooth + amp_envelope: { attack: 60, decay: 100, sustain: 800, release: 300, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 52 + _comment: Synth Strings 2 + waveform: square_40 + amp_envelope: { attack: 80, decay: 100, sustain: 780, release: 300, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 53 + _comment: Choir Aahs + waveform: sine + amp_envelope: { attack: 120, decay: 50, sustain: 900, release: 300, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 6 } + - instrument: 54 + _comment: Voice Oohs + waveform: sine + amp_envelope: { attack: 140, decay: 50, sustain: 900, release: 300, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 5 } + - instrument: 55 + _comment: Synth Voice + waveform: square_20 + amp_envelope: { attack: 90, decay: 50, sustain: 870, release: 250, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 7 } + - instrument: 56 + _comment: Orchestra Hit + waveform: sawtooth + amp_envelope: { attack: 2, decay: 300, sustain: 0, release: 100, amplitude: 960 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 57 + _comment: Trumpet + waveform: sawtooth + amp_envelope: { attack: 20, decay: 50, sustain: 900, release: 100, amplitude: 930 } + pitch_envelope: { attack: 12, decay: 60, sustain: 0, release: 50, amplitude: 40 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 58 + _comment: Trombone + waveform: sawtooth + amp_envelope: { attack: 30, decay: 50, sustain: 880, release: 150, amplitude: 920 } + pitch_envelope: { attack: 15, decay: 60, sustain: 0, release: 60, amplitude: 35 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 59 + _comment: Tuba + waveform: sawtooth + amp_envelope: { attack: 30, decay: 50, sustain: 870, release: 150, amplitude: 910 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 60 + _comment: Muted Trumpet + waveform: square_20 + amp_envelope: { attack: 15, decay: 50, sustain: 800, release: 80, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 61 + _comment: French Horn + waveform: sawtooth + amp_envelope: { attack: 55, decay: 50, sustain: 880, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 6 } + - instrument: 62 + _comment: Brass Section + waveform: sawtooth + amp_envelope: { attack: 15, decay: 50, sustain: 900, release: 100, amplitude: 940 } + pitch_envelope: { attack: 10, decay: 50, sustain: 0, release: 40, amplitude: 30 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 63 + _comment: Synth Brass 1 + waveform: square_50 + amp_envelope: { attack: 10, decay: 100, sustain: 850, release: 150, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 64 + _comment: Synth Brass 2 + waveform: sawtooth + amp_envelope: { attack: 10, decay: 100, sustain: 850, release: 150, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 65 + _comment: Soprano Sax + waveform: square_30 + amp_envelope: { attack: 20, decay: 50, sustain: 850, release: 100, amplitude: 890 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 9 } + - instrument: 66 + _comment: Alto Sax + waveform: square_30 + amp_envelope: { attack: 20, decay: 50, sustain: 850, release: 100, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 9 } + - instrument: 67 + _comment: Tenor Sax + waveform: square_30 + amp_envelope: { attack: 25, decay: 50, sustain: 840, release: 120, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 8 } + - instrument: 68 + _comment: Baritone Sax + waveform: square_30 + amp_envelope: { attack: 30, decay: 50, sustain: 830, release: 150, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 7 } + - instrument: 69 + _comment: Oboe + waveform: square_10 + amp_envelope: { attack: 20, decay: 30, sustain: 870, release: 80, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 7 } + - instrument: 70 + _comment: English Horn + waveform: square_20 + amp_envelope: { attack: 25, decay: 30, sustain: 860, release: 100, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 6 } + - instrument: 71 + _comment: Bassoon + waveform: square_20 + amp_envelope: { attack: 30, decay: 30, sustain: 850, release: 120, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 5 } + - instrument: 72 + _comment: Clarinet + waveform: square_50 + amp_envelope: { attack: 20, decay: 30, sustain: 870, release: 100, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 7 } + - instrument: 73 + _comment: Piccolo + waveform: triangle + amp_envelope: { attack: 15, decay: 20, sustain: 880, release: 80, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 6 } + - instrument: 74 + _comment: Flute + waveform: sine + amp_envelope: { attack: 20, decay: 20, sustain: 880, release: 100, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 5, amplitude: 20 } + pitch_lfo: { frequency: 5, amplitude: 9 } + - instrument: 75 + _comment: Recorder + waveform: triangle + amp_envelope: { attack: 15, decay: 20, sustain: 870, release: 80, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 6 } + - instrument: 76 + _comment: Pan Flute + waveform: sine + amp_envelope: { attack: 35, decay: 20, sustain: 870, release: 120, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 8 } + - instrument: 77 + _comment: Blown Bottle + waveform: noise_tunable + amp_envelope: { attack: 25, decay: 120, sustain: 650, release: 180, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 78 + _comment: Shakuhachi + waveform: noise_tunable + amp_envelope: { attack: 60, decay: 60, sustain: 750, release: 220, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 12 } + - instrument: 79 + _comment: Whistle + waveform: sine + amp_envelope: { attack: 15, decay: 0, sustain: 900, release: 80, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 10 } + - instrument: 80 + _comment: Ocarina + waveform: sine + amp_envelope: { attack: 20, decay: 20, sustain: 880, release: 100, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 8 } + - instrument: 81 + _comment: Lead 1 (square) + waveform: square_50 + amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 82 + _comment: Lead 2 (sawtooth) + waveform: sawtooth + amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 83 + _comment: Lead 3 (calliope) + waveform: triangle + amp_envelope: { attack: 10, decay: 50, sustain: 800, release: 100, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 84 + _comment: Lead 4 (chiff) + waveform: cycle_16 + amp_envelope: { attack: 5, decay: 120, sustain: 700, release: 100, amplitude: 880 } + pitch_envelope: { attack: 6, decay: 80, sustain: 0, release: 50, amplitude: 30 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 85 + _comment: Lead 5 (charang) + waveform: cycle_32 + amp_envelope: { attack: 5, decay: 100, sustain: 800, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 3, amplitude: 6 } + - instrument: 86 + _comment: Lead 6 (voice) + waveform: square_20 + amp_envelope: { attack: 30, decay: 50, sustain: 850, release: 150, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 8 } + - instrument: 87 + _comment: Lead 7 (fifths) + waveform: square_50 + amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 910 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 88 + _comment: Lead 8 (bass+lead) + waveform: sawtooth + amp_envelope: { attack: 5, decay: 100, sustain: 800, release: 100, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 89 + _comment: Pad 1 (new age) + waveform: sine + amp_envelope: { attack: 220, decay: 100, sustain: 800, release: 450, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 90 + _comment: Pad 2 (warm) + waveform: triangle + amp_envelope: { attack: 170, decay: 100, sustain: 850, release: 450, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 91 + _comment: Pad 3 (polysynth) + waveform: sawtooth + amp_envelope: { attack: 110, decay: 100, sustain: 800, release: 380, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 92 + _comment: Pad 4 (choir) + waveform: sine + amp_envelope: { attack: 160, decay: 50, sustain: 900, release: 430, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 3, amplitude: 6 } + - instrument: 93 + _comment: Pad 5 (bowed) + waveform: triangle + amp_envelope: { attack: 220, decay: 0, sustain: 900, release: 520, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 5 } + - instrument: 94 + _comment: Pad 6 (metallic) + waveform: cycle_16 + amp_envelope: { attack: 110, decay: 200, sustain: 700, release: 420, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 95 + _comment: Pad 7 (halo) + waveform: sine + amp_envelope: { attack: 270, decay: 100, sustain: 800, release: 520, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 3, amplitude: 9 } + - instrument: 96 + _comment: Pad 8 (sweep) + waveform: sawtooth + amp_envelope: { attack: 220, decay: 200, sustain: 700, release: 520, amplitude: 830 } + pitch_envelope: { attack: 60, decay: 350, sustain: 0, release: 220, amplitude: 60 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 97 + _comment: FX 1 (rain) + waveform: noise_tunable + amp_envelope: { attack: 55, decay: 200, sustain: 500, release: 420, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 98 + _comment: FX 2 (soundtrack) + waveform: sawtooth + amp_envelope: { attack: 110, decay: 100, sustain: 800, release: 420, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 99 + _comment: FX 3 (crystal) + waveform: sine + amp_envelope: { attack: 20, decay: 550, sustain: 80, release: 420, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 100 + _comment: FX 4 (atmosphere) + waveform: triangle + amp_envelope: { attack: 160, decay: 200, sustain: 700, release: 520, amplitude: 810 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 101 + _comment: FX 5 (brightness) + waveform: sawtooth + amp_envelope: { attack: 30, decay: 100, sustain: 800, release: 320, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 102 + _comment: FX 6 (goblins) + waveform: cycle_32 + amp_envelope: { attack: 110, decay: 100, sustain: 700, release: 420, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 3, amplitude: 18 } + - instrument: 103 + _comment: FX 7 (echoes) + waveform: sine + amp_envelope: { attack: 55, decay: 300, sustain: 400, release: 520, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 104 + _comment: FX 8 (sci-fi) + waveform: cycle_64 + amp_envelope: { attack: 85, decay: 200, sustain: 600, release: 420, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 2, amplitude: 22 } + - instrument: 105 + _comment: Sitar + waveform: sawtooth + amp_envelope: { attack: 5, decay: 550, sustain: 180, release: 280, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 12 } + - instrument: 106 + _comment: Banjo + waveform: triangle + amp_envelope: { attack: 3, decay: 380, sustain: 80, release: 180, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 107 + _comment: Shamisen + waveform: sawtooth + amp_envelope: { attack: 3, decay: 320, sustain: 80, release: 130, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 108 + _comment: Koto + waveform: triangle + amp_envelope: { attack: 5, decay: 580, sustain: 80, release: 280, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 109 + _comment: Kalimba + waveform: sine + amp_envelope: { attack: 3, decay: 650, sustain: 0, release: 180, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 110 + _comment: Bag Pipe + waveform: square_50 + amp_envelope: { attack: 50, decay: 0, sustain: 900, release: 100, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 4, amplitude: 6 } + - instrument: 111 + _comment: Fiddle + waveform: sawtooth + amp_envelope: { attack: 30, decay: 0, sustain: 900, release: 150, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 9 } + - instrument: 112 + _comment: Shanai + waveform: square_10 + amp_envelope: { attack: 20, decay: 50, sustain: 860, release: 100, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 9 } + - instrument: 113 + _comment: Tinkle Bell + waveform: sine + amp_envelope: { attack: 2, decay: 850, sustain: 0, release: 180, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 114 + _comment: Agogo + waveform: triangle + amp_envelope: { attack: 2, decay: 480, sustain: 0, release: 130, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 115 + _comment: Steel Drums + waveform: sine + amp_envelope: { attack: 5, decay: 620, sustain: 80, release: 180, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 116 + _comment: Woodblock + waveform: cycle_16 + amp_envelope: { attack: 2, decay: 90, sustain: 0, release: 40, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 117 + _comment: Taiko Drum + waveform: triangle + amp_envelope: { attack: 2, decay: 380, sustain: 0, release: 180, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 118 + _comment: Melodic Tom + waveform: triangle + amp_envelope: { attack: 3, decay: 280, sustain: 80, release: 130, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 119 + _comment: Synth Drum + waveform: square_50 + amp_envelope: { attack: 3, decay: 280, sustain: 0, release: 90, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 120 + _comment: Reverse Cymbal + waveform: noise + amp_envelope: { attack: 900, decay: 0, sustain: 900, release: 50, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 121 + _comment: Guitar Fret Noise + waveform: noise_tunable + amp_envelope: { attack: 2, decay: 90, sustain: 0, release: 25, amplitude: 600 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 122 + _comment: Breath Noise + waveform: noise_tunable + amp_envelope: { attack: 10, decay: 50, sustain: 400, release: 100, amplitude: 600 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 123 + _comment: Seashore + waveform: noise + amp_envelope: { attack: 200, decay: 0, sustain: 600, release: 400, amplitude: 550 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 124 + _comment: Bird Tweet + waveform: sine + amp_envelope: { attack: 5, decay: 100, sustain: 700, release: 100, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 9, amplitude: 35 } + - instrument: 125 + _comment: Telephone Ring + waveform: square_50 + amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 2, amplitude: 900 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 126 + _comment: Helicopter + waveform: cycle_16 + amp_envelope: { attack: 100, decay: 0, sustain: 700, release: 200, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 4, amplitude: 20 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 127 + _comment: Applause + waveform: noise + amp_envelope: { attack: 220, decay: 100, sustain: 600, release: 400, amplitude: 650 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 128 + _comment: Gunshot + waveform: noise + amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } +drum_instruments: + - note: 35 + _comment: Acoustic Bass Drum + start_freq: 150 + start_vol: 1024 + steps: + - { waveform: sine, target_freq: 80, target_vol: 900, duration: 30 } + - { waveform: sine, target_freq: 50, target_vol: 400, duration: 40 } + - { waveform: sine, target_freq: 40, target_vol: 0, duration: 60 } + - note: 36 + _comment: Bass Drum 1 + start_freq: 120 + start_vol: 1024 + steps: + - { waveform: sine, target_freq: 70, target_vol: 850, duration: 25 } + - { waveform: sine, target_freq: 45, target_vol: 350, duration: 35 } + - { waveform: sine, target_freq: 35, target_vol: 0, duration: 50 } + - note: 37 + _comment: Side Stick + start_freq: 1200 + start_vol: 900 + steps: + - { waveform: cycle_16, target_freq: 900, target_vol: 400, duration: 15 } + - { waveform: cycle_16, target_freq: 500, target_vol: 0, duration: 25 } + - note: 38 + _comment: Acoustic Snare + start_freq: 800 + start_vol: 1024 + steps: + - { waveform: noise, target_freq: 800, target_vol: 700, duration: 18 } + - { waveform: noise, target_freq: 800, target_vol: 300, duration: 35 } + - { waveform: noise, target_freq: 800, target_vol: 0, duration: 40 } + - note: 39 + _comment: Hand Clap + start_freq: 2000 + start_vol: 1024 + steps: + - { waveform: noise, target_freq: 2000, target_vol: 600, duration: 12 } + - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 25 } + - note: 40 + _comment: Electric Snare + start_freq: 1000 + start_vol: 1000 + steps: + - { waveform: noise, target_freq: 1000, target_vol: 600, duration: 14 } + - { waveform: noise, target_freq: 1000, target_vol: 200, duration: 28 } + - { waveform: noise, target_freq: 1000, target_vol: 0, duration: 30 } + - note: 41 + _comment: Low Floor Tom + start_freq: 120 + start_vol: 1000 + steps: + - { waveform: triangle, target_freq: 80, target_vol: 700, duration: 40 } + - { waveform: triangle, target_freq: 60, target_vol: 200, duration: 80 } + - { waveform: triangle, target_freq: 50, target_vol: 0, duration: 80 } + - note: 42 + _comment: Closed Hi-Hat + start_freq: 8000 + start_vol: 800 + steps: + - { waveform: noise, target_freq: 8000, target_vol: 400, duration: 8 } + - { waveform: noise, target_freq: 8000, target_vol: 0, duration: 12 } + - note: 43 + _comment: High Floor Tom + start_freq: 150 + start_vol: 1000 + steps: + - { waveform: triangle, target_freq: 100, target_vol: 700, duration: 35 } + - { waveform: triangle, target_freq: 75, target_vol: 200, duration: 70 } + - { waveform: triangle, target_freq: 60, target_vol: 0, duration: 70 } + - note: 44 + _comment: Pedal Hi-Hat + start_freq: 7000 + start_vol: 700 + steps: + - { waveform: noise, target_freq: 7000, target_vol: 280, duration: 7 } + - { waveform: noise, target_freq: 7000, target_vol: 0, duration: 10 } + - note: 45 + _comment: Low Tom + start_freq: 180 + start_vol: 1000 + steps: + - { waveform: triangle, target_freq: 120, target_vol: 700, duration: 30 } + - { waveform: triangle, target_freq: 90, target_vol: 200, duration: 60 } + - { waveform: triangle, target_freq: 70, target_vol: 0, duration: 60 } + - note: 46 + _comment: Open Hi-Hat + start_freq: 8000 + start_vol: 800 + steps: + - { waveform: noise, target_freq: 8000, target_vol: 600, duration: 45 } + - { waveform: noise, target_freq: 8000, target_vol: 200, duration: 90 } + - { waveform: noise, target_freq: 8000, target_vol: 0, duration: 100 } + - note: 47 + _comment: Low-Mid Tom + start_freq: 220 + start_vol: 1000 + steps: + - { waveform: triangle, target_freq: 150, target_vol: 700, duration: 28 } + - { waveform: triangle, target_freq: 110, target_vol: 200, duration: 55 } + - { waveform: triangle, target_freq: 85, target_vol: 0, duration: 55 } + - note: 48 + _comment: Hi-Mid Tom + start_freq: 280 + start_vol: 1000 + steps: + - { waveform: triangle, target_freq: 190, target_vol: 700, duration: 25 } + - { waveform: triangle, target_freq: 140, target_vol: 200, duration: 50 } + - { waveform: triangle, target_freq: 110, target_vol: 0, duration: 50 } + - note: 49 + _comment: Crash Cymbal 1 + start_freq: 6000 + start_vol: 1000 + steps: + - { waveform: noise, target_freq: 6000, target_vol: 700, duration: 70 } + - { waveform: noise, target_freq: 6000, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 6000, target_vol: 0, duration: 300 } + - note: 50 + _comment: High Tom + start_freq: 350 + start_vol: 1000 + steps: + - { waveform: triangle, target_freq: 240, target_vol: 700, duration: 20 } + - { waveform: triangle, target_freq: 180, target_vol: 200, duration: 40 } + - { waveform: triangle, target_freq: 140, target_vol: 0, duration: 40 } + - note: 51 + _comment: Ride Cymbal 1 + start_freq: 5000 + start_vol: 700 + steps: + - { waveform: noise, target_freq: 5000, target_vol: 380, duration: 35 } + - { waveform: noise, target_freq: 5000, target_vol: 100, duration: 75 } + - { waveform: noise, target_freq: 5000, target_vol: 0, duration: 100 } + - note: 52 + _comment: Chinese Cymbal + start_freq: 7000 + start_vol: 950 + steps: + - { waveform: noise, target_freq: 7000, target_vol: 700, duration: 45 } + - { waveform: noise, target_freq: 7000, target_vol: 400, duration: 150 } + - { waveform: noise, target_freq: 7000, target_vol: 0, duration: 200 } + - note: 53 + _comment: Ride Bell + start_freq: 3000 + start_vol: 800 + steps: + - { waveform: sine, target_freq: 2800, target_vol: 500, duration: 70 } + - { waveform: sine, target_freq: 2600, target_vol: 100, duration: 180 } + - { waveform: sine, target_freq: 2400, target_vol: 0, duration: 200 } + - note: 54 + _comment: Tambourine + start_freq: 5000 + start_vol: 800 + steps: + - { waveform: noise, target_freq: 5000, target_vol: 400, duration: 18 } + - { waveform: noise, target_freq: 5000, target_vol: 0, duration: 28 } + - note: 55 + _comment: Splash Cymbal + start_freq: 6500 + start_vol: 900 + steps: + - { waveform: noise, target_freq: 6500, target_vol: 600, duration: 25 } + - { waveform: noise, target_freq: 6500, target_vol: 200, duration: 70 } + - { waveform: noise, target_freq: 6500, target_vol: 0, duration: 80 } + - note: 56 + _comment: Cowbell + start_freq: 800 + start_vol: 900 + steps: + - { waveform: cycle_16, target_freq: 700, target_vol: 600, duration: 45 } + - { waveform: cycle_16, target_freq: 600, target_vol: 200, duration: 90 } + - { waveform: cycle_16, target_freq: 550, target_vol: 0, duration: 100 } + - note: 57 + _comment: Crash Cymbal 2 + start_freq: 5500 + start_vol: 1000 + steps: + - { waveform: noise, target_freq: 5500, target_vol: 700, duration: 70 } + - { waveform: noise, target_freq: 5500, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 5500, target_vol: 0, duration: 300 } + - note: 58 + _comment: Vibraslap + start_freq: 400 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 300, target_vol: 600, duration: 25 } + - { waveform: noise_tunable, target_freq: 300, target_vol: 300, duration: 90 } + - { waveform: noise_tunable, target_freq: 200, target_vol: 0, duration: 100 } + - note: 59 + _comment: Ride Cymbal 2 + start_freq: 4500 + start_vol: 700 + steps: + - { waveform: noise, target_freq: 4500, target_vol: 380, duration: 38 } + - { waveform: noise, target_freq: 4500, target_vol: 100, duration: 75 } + - { waveform: noise, target_freq: 4500, target_vol: 0, duration: 100 } + - note: 60 + _comment: Hi Bongo + start_freq: 500 + start_vol: 950 + steps: + - { waveform: triangle, target_freq: 380, target_vol: 600, duration: 18 } + - { waveform: triangle, target_freq: 280, target_vol: 200, duration: 38 } + - { waveform: triangle, target_freq: 220, target_vol: 0, duration: 40 } + - note: 61 + _comment: Low Bongo + start_freq: 350 + start_vol: 950 + steps: + - { waveform: triangle, target_freq: 260, target_vol: 600, duration: 22 } + - { waveform: triangle, target_freq: 190, target_vol: 200, duration: 45 } + - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } + - note: 62 + _comment: Mute Hi Conga + start_freq: 600 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 420, target_vol: 400, duration: 12 } + - { waveform: triangle, target_freq: 320, target_vol: 0, duration: 22 } + - note: 63 + _comment: Open Hi Conga + start_freq: 600 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 520, target_vol: 600, duration: 28 } + - { waveform: triangle, target_freq: 420, target_vol: 200, duration: 55 } + - { waveform: triangle, target_freq: 320, target_vol: 0, duration: 60 } + - note: 64 + _comment: Low Conga + start_freq: 400 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 330, target_vol: 600, duration: 32 } + - { waveform: triangle, target_freq: 270, target_vol: 200, duration: 65 } + - { waveform: triangle, target_freq: 210, target_vol: 0, duration: 70 } + - note: 65 + _comment: High Timbale + start_freq: 450 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 320, target_vol: 500, duration: 18 } + - { waveform: noise_tunable, target_freq: 300, target_vol: 200, duration: 38 } + - { waveform: noise_tunable, target_freq: 200, target_vol: 0, duration: 40 } + - note: 66 + _comment: Low Timbale + start_freq: 280 + start_vol: 900 + steps: + - { waveform: triangle, target_freq: 210, target_vol: 500, duration: 22 } + - { waveform: noise_tunable, target_freq: 200, target_vol: 200, duration: 48 } + - { waveform: noise_tunable, target_freq: 120, target_vol: 0, duration: 50 } + - note: 67 + _comment: High Agogo + start_freq: 1200 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 1050, target_vol: 400, duration: 28 } + - { waveform: triangle, target_freq: 850, target_vol: 0, duration: 55 } + - note: 68 + _comment: Low Agogo + start_freq: 900 + start_vol: 850 + steps: + - { waveform: triangle, target_freq: 780, target_vol: 400, duration: 32 } + - { waveform: triangle, target_freq: 620, target_vol: 0, duration: 65 } + - note: 69 + _comment: Cabasa + start_freq: 4000 + start_vol: 700 + steps: + - { waveform: noise, target_freq: 4000, target_vol: 300, duration: 14 } + - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 28 } + - note: 70 + _comment: Maracas + start_freq: 5000 + start_vol: 700 + steps: + - { waveform: noise, target_freq: 5000, target_vol: 280, duration: 9 } + - { waveform: noise, target_freq: 5000, target_vol: 0, duration: 18 } + - note: 75 + _comment: Claves + start_freq: 1500 + start_vol: 900 + steps: + - { waveform: cycle_16, target_freq: 1300, target_vol: 400, duration: 12 } + - { waveform: cycle_16, target_freq: 1000, target_vol: 0, duration: 25 } + - note: 76 + _comment: Hi Wood Block + start_freq: 1800 + start_vol: 900 + steps: + - { waveform: cycle_16, target_freq: 1500, target_vol: 400, duration: 12 } + - { waveform: cycle_16, target_freq: 1100, target_vol: 0, duration: 22 } + - note: 77 + _comment: Low Wood Block + start_freq: 1200 + start_vol: 900 + steps: + - { waveform: cycle_16, target_freq: 1000, target_vol: 400, duration: 15 } + - { waveform: cycle_16, target_freq: 750, target_vol: 0, duration: 28 } + - note: 80 + _comment: Mute Triangle + start_freq: 4000 + start_vol: 800 + steps: + - { waveform: sine, target_freq: 3800, target_vol: 300, duration: 18 } + - { waveform: sine, target_freq: 3600, target_vol: 0, duration: 28 } + - note: 81 + _comment: Open Triangle + start_freq: 4000 + start_vol: 800 + steps: + - { waveform: sine, target_freq: 3900, target_vol: 500, duration: 90 } + - { waveform: sine, target_freq: 3750, target_vol: 200, duration: 280 } + - { waveform: sine, target_freq: 3600, target_vol: 0, duration: 300 } diff --git a/requirements.txt b/requirements.txt index f7f28e6..e2e3a44 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ mido +pyyaml diff --git a/src/arcade/music_types.py b/src/arcade/music_types.py index f9f9017..a1f1db3 100644 --- a/src/arcade/music_types.py +++ b/src/arcade/music_types.py @@ -88,8 +88,8 @@ @dataclass class Instrument: waveform: int - amp_envelope: Envelope octave: int + amp_envelope: Envelope pitch_envelope: Optional[Envelope] = None amp_lfo: Optional[LFO] = None pitch_lfo: Optional[LFO] = None diff --git a/src/converter/instruments.py b/src/converter/instruments.py new file mode 100644 index 0000000..439d8e8 --- /dev/null +++ b/src/converter/instruments.py @@ -0,0 +1,131 @@ +import logging +from dataclasses import dataclass +from enum import IntEnum +from typing import Dict + +from yaml import safe_load + +from arcade.music_types import DrumInstrument, DrumSoundStep, Envelope, Instrument, LFO +from utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +@dataclass +class InstrumentParameterMapping: + # The integer is the general MIDI instrument + melodic_instruments: Dict[int, Instrument] + # The integer is the MIDI drum kit note + drum_instruments: Dict[int, DrumInstrument] + + +class WaveForm(IntEnum): + TRIANGLE = 1 + SAWTOOTH = 2 + SINE = 3 + NOISE_TUNABLE = 4 + NOISE = 5 + SQUARE_10 = 11 + SQUARE_20 = 12 + SQUARE_30 = 13 + SQUARE_40 = 14 + SQUARE_50 = 15 # aka square + CYCLE_16 = 16 + CYCLE_32 = 17 + CYCLE_64 = 18 # aka noise_tunable_2 + + +def waveform_from_str(w: str) -> WaveForm: + """ + Takes a string and returns the correct waveform integer. + + :param w: A string like "triangle" or "sine". + :return: A `WaveForm` enum value. + """ + w = w.lower() + if w == "square": + w = "square_50" + if w == "noise_tunable_2": + w = "cycle_64" + return { + "triangle": WaveForm.TRIANGLE, + "sawtooth": WaveForm.SAWTOOTH, + "sine": WaveForm.SINE, + "noise_tunable": WaveForm.NOISE_TUNABLE, + "noise": WaveForm.NOISE, + "square_10": WaveForm.SQUARE_10, + "square_20": WaveForm.SQUARE_20, + "square_30": WaveForm.SQUARE_30, + "square_40": WaveForm.SQUARE_40, + "square_50": WaveForm.SQUARE_50, + "cycle_16": WaveForm.CYCLE_16, + "cycle_32": WaveForm.CYCLE_32, + "cycle_64": WaveForm.CYCLE_64, + }[w] + + +def load_instrument_params(yaml_text: str) -> InstrumentParameterMapping: + """ + Take a YAML file specifying instrument data and return an instrument parameter + mapping. For melodic instruments, the octave has been set to 0, duplicate as + necessary for the full MIDI range (use octave offset 2 and 7 for full range) + + :param yaml_text: String holding the YAML file's text. + :return: An `InstrumentParameterMapping` object. + """ + logger.debug(f"Loading instrument parameters from {len(yaml_text)} characters of " + f"YAML text") + data = safe_load(yaml_text) + + mapping = InstrumentParameterMapping(melodic_instruments={}, drum_instruments={}) + + logger.debug(f"Creating mappings for {len(data["melodic_instruments"])} melodic " + f"instruments") + for instr in data["melodic_instruments"]: + mapping.melodic_instruments[instr["instrument"]] = Instrument( + waveform=waveform_from_str(instr["waveform"]), + # Each note can range from 0-63, so we'll have two tracks, each with the + # same two instruments but at different octave offsets + octave=0, + amp_envelope=Envelope( + attack=instr["amp_envelope"]["attack"], + decay=instr["amp_envelope"]["decay"], + sustain=instr["amp_envelope"]["sustain"], + release=instr["amp_envelope"]["release"], + amplitude=instr["amp_envelope"]["amplitude"], + ), + pitch_envelope=Envelope( + attack=instr["pitch_envelope"]["attack"], + decay=instr["pitch_envelope"]["decay"], + sustain=instr["pitch_envelope"]["sustain"], + release=instr["pitch_envelope"]["release"], + amplitude=instr["pitch_envelope"]["amplitude"], + ), + amp_lfo=LFO( + frequency=instr["amp_lfo"]["frequency"], + amplitude=instr["amp_lfo"]["amplitude"], + ), + pitch_lfo=LFO( + frequency=instr["pitch_lfo"]["frequency"], + amplitude=instr["pitch_lfo"]["amplitude"], + ) + ) + + logger.debug(f"Creating mappings for {len(data["drum_instruments"])} drum " + f"instruments") + for sample in data["drum_instruments"]: + mapping.drum_instruments[sample["note"]] = DrumInstrument( + name=sample["_comment"], + start_frequency=sample["start_freq"], + start_volume=sample["start_vol"], + steps=[ + DrumSoundStep( + waveform=waveform_from_str(step["waveform"]), + frequency=step["target_freq"], + volume=step["target_vol"], + duration=step["duration"], + ) for step in sample["steps"] + ] + ) + + return mapping diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index 593e8ec..b310d2b 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -6,6 +6,7 @@ from mido import Message, MidiFile, tick2second from arcade.music_types import Song +from converter.instruments import InstrumentParameterMapping from utils.logger import create_logger logger = create_logger(name=__name__, level=logging.INFO) @@ -356,15 +357,52 @@ def timeline_group_messages(timeline: List[AbsoluteTimeMessageWithInstrument]) - return timeline_with_complete_notes -def convert_midi_to_song(midi_song: MidiFile) -> Song: +def find_all_melodic_instruments(timeline: List[AbsoluteCompleteNote]) -> List[int]: + """ + Search the timeline for all unique melodic instruments. + + :param timeline: A list of `AbsoluteTimeMessage` objects. + :return: A list of ints, representing what general MIDI instruments are in the song. + """ + logger.debug("Finding all melodic instruments in the timeline") + + return list(set([m.instrument for m in timeline if not m.is_drum])) + + +def find_is_drum_used(timeline: List[AbsoluteCompleteNote]) -> bool: + """ + Search the timeline if any drum instruments are used. Since we only support the + default drum kit, it's a boolean result. + + :param timeline: A list of `AbsoluteTimeMessage` objects. + :return: A bool on whether a drum instrument is used in the song. + """ + logger.debug("Finding if any drum instruments are used in the timeline") + + return any([m.is_drum for m in timeline]) + + +def convert_midi_to_song(midi_song: MidiFile, + mapping: InstrumentParameterMapping) -> Song: """ Convert a MIDI file into a MakeCode Arcade song. - :param midi_song: `MidiFile` object. + :param midi_song: A `MidiFile` object. + :param mapping: An `InstrumentParameterMapping` object, loaded from + `load_instrument_params`. :return: MakeCode Arcade `Song` object. """ logger.debug("Converting MIDI file into MakeCode Arcade song") - global_timeline = timeline_build(midi_song) - global_timeline = timeline_find_instrument_data(global_timeline) - global_timeline = timeline_group_messages(global_timeline) + global_timeline: List[AbsoluteTimeMessage] = timeline_build(midi_song) + global_timeline: List[ + AbsoluteTimeMessageWithInstrument] = timeline_find_instrument_data( + global_timeline) + global_timeline: List[AbsoluteCompleteNote] = timeline_group_messages( + global_timeline) + + melodics_used = find_all_melodic_instruments(global_timeline) + drum_used = find_is_drum_used(global_timeline) + logger.debug(f"Song used {len(melodics_used)} melodic instruments and " + f"{"used" if drum_used else "did not use"} the drum instrument") + pass diff --git a/src/main.py b/src/main.py index 0b469cd..718a3b4 100644 --- a/src/main.py +++ b/src/main.py @@ -5,14 +5,15 @@ from mido import MidiFile from arcade.music import encode_song_to_hex +from converter.instruments import load_instrument_params from converter.midi_to_song import convert_midi_to_song from utils.logger import create_logger, set_all_stdout_logger_levels parser = ArgumentParser(description="Convert a MIDI file to a MakeCode Arcade song.") parser.add_argument("--input", "-i", type=Path, - help="Input MIDI file." - # ", otherwise will read from stdin." - ) + help="Input MIDI file.") +parser.add_argument("--input-instrument-params", "-p", type=Path, + help="Input instrument parameter mapping file.") # parser.add_argument("--output", "-o", type=Path, # help="Output TypeScript file path, otherwise will write to stdout.") parser.add_argument("--debug", action="store_const", @@ -30,7 +31,14 @@ mid = MidiFile(input_path) logger.debug(f"Found {len(mid.tracks)} tracks, length of {mid.length}s") -song = convert_midi_to_song(mid) +input_instrument_param_path = Path(args.input_instrument_params) +logger.debug(f"Reading instrument parameter mapping from {input_instrument_param_path}") + +mapping = load_instrument_params(input_instrument_param_path.read_text()) +logger.debug(f"Mapped {len(mapping.melodic_instruments)} melodic instruments and " + f"{len(mapping.drum_instruments)} drum instruments") + +song = convert_midi_to_song(mid, mapping) h = encode_song_to_hex(song) logger.info("Finished converting MIDI file") print(f"hex`{h}`") From aeeac2985d455e80816e8ab4bf3de3ce554d8645 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Thu, 28 May 2026 23:12:35 -0400 Subject: [PATCH 09/22] Update README --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 2287022..3933e7d 100644 --- a/README.md +++ b/README.md @@ -2,5 +2,3 @@ A Python tool to convert a MIDI file to a MakeCode Arcade song! (Work in progress) - -Some bug squashing may be needed but otherwise this tool is complete. From 4c08491279a9c25fe8b9a11da4825e235c84cd76 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sat, 30 May 2026 00:56:18 -0400 Subject: [PATCH 10/22] Update instrument_params_2.yaml with GM2 drum notes, assemble song with the correct tracks for all needed melodic and drum instruments --- example/instrument_params_2.yaml | 134 ++++++++++++++++++++++++++++++- src/converter/instruments.py | 4 +- src/converter/midi_to_song.py | 116 +++++++++++++++++++++++--- 3 files changed, 238 insertions(+), 16 deletions(-) diff --git a/example/instrument_params_2.yaml b/example/instrument_params_2.yaml index 2979c55..a2f77a4 100644 --- a/example/instrument_params_2.yaml +++ b/example/instrument_params_2.yaml @@ -34,10 +34,11 @@ https://arcade.makecode.com/developer/sound > like a noise-distorted square wave, with short cycles being more regular and > long cycles being noisier. " -_comment2: 'MakeCode Arcade GM instrument parameter map. All envelope times in ms. sustain/amplitude values 0-1024. LFO frequency - in Hz. Waveforms: triangle=1 sawtooth=2 sine=3 noise_tunable=4 noise=5 square_10=11 square_20=12 square_30=13 square_40=14 - square_50=15 cycle_16=16 cycle_32=17 cycle_64=18. noise ignores pitch. noise_tunable is pitch-dependent (high=white noise, - low=crackling). cycle_N are periodic pseudo-random patterns (shorter = more regular).' +_comment2: 'MakeCode Arcade GM1/GM2 instrument parameter map. All envelope times in ms. sustain/amplitude values 0-1024. LFO + frequency in Hz. Waveforms: triangle=1 sawtooth=2 sine=3 noise_tunable=4 noise=5 square_10=11 square_20=12 square_30=13 + square_40=14 square_50=15 cycle_16=16 cycle_32=17 cycle_64=18. noise ignores pitch. noise_tunable is pitch-dependent (high=white + noise, low=crackling). cycle_N are periodic pseudo-random patterns (shorter = more regular). Drum notes: GM2 extended range + 27-87 (GM1 was 35-81). Notes 71-74 and 78-79 are unassigned in both GM1 and GM2.' melodic_instruments: - instrument: 1 _comment: Acoustic Grand Piano @@ -936,6 +937,66 @@ melodic_instruments: amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } drum_instruments: + - note: 27 + _comment: High Q + start_freq: 3500 + start_vol: 900 + steps: + - { waveform: cycle_16, target_freq: 2800, target_vol: 500, duration: 5 } + - { waveform: cycle_16, target_freq: 2000, target_vol: 0, duration: 8 } + - note: 28 + _comment: Slap + start_freq: 300 + start_vol: 1000 + steps: + - { waveform: noise_tunable, target_freq: 400, target_vol: 700, duration: 12 } + - { waveform: noise_tunable, target_freq: 250, target_vol: 200, duration: 30 } + - { waveform: noise_tunable, target_freq: 150, target_vol: 0, duration: 35 } + - note: 29 + _comment: Scratch Push + start_freq: 200 + start_vol: 950 + steps: + - { waveform: noise_tunable, target_freq: 600, target_vol: 800, duration: 30 } + - { waveform: noise_tunable, target_freq: 1200, target_vol: 500, duration: 40 } + - { waveform: noise_tunable, target_freq: 2000, target_vol: 0, duration: 30 } + - note: 30 + _comment: Scratch Pull + start_freq: 2000 + start_vol: 950 + steps: + - { waveform: noise_tunable, target_freq: 1200, target_vol: 800, duration: 30 } + - { waveform: noise_tunable, target_freq: 500, target_vol: 500, duration: 40 } + - { waveform: noise_tunable, target_freq: 200, target_vol: 0, duration: 30 } + - note: 31 + _comment: Sticks + start_freq: 4000 + start_vol: 850 + steps: + - { waveform: cycle_16, target_freq: 3200, target_vol: 400, duration: 6 } + - { waveform: cycle_16, target_freq: 2400, target_vol: 0, duration: 10 } + - note: 32 + _comment: Square Click + start_freq: 1800 + start_vol: 900 + steps: + - { waveform: cycle_16, target_freq: 1400, target_vol: 500, duration: 8 } + - { waveform: cycle_16, target_freq: 1000, target_vol: 0, duration: 12 } + - note: 33 + _comment: Metronome Click + start_freq: 1400 + start_vol: 870 + steps: + - { waveform: cycle_16, target_freq: 1100, target_vol: 450, duration: 8 } + - { waveform: cycle_16, target_freq: 800, target_vol: 0, duration: 14 } + - note: 34 + _comment: Metronome Bell + start_freq: 2800 + start_vol: 870 + steps: + - { waveform: sine, target_freq: 2600, target_vol: 500, duration: 40 } + - { waveform: sine, target_freq: 2400, target_vol: 150, duration: 150 } + - { waveform: sine, target_freq: 2200, target_vol: 0, duration: 150 } - note: 35 _comment: Acoustic Bass Drum start_freq: 150 @@ -1235,6 +1296,23 @@ drum_instruments: steps: - { waveform: cycle_16, target_freq: 1000, target_vol: 400, duration: 15 } - { waveform: cycle_16, target_freq: 750, target_vol: 0, duration: 28 } + - note: 78 + _comment: Mute Cuica + start_freq: 600 + start_vol: 950 + steps: + - { waveform: noise_tunable, target_freq: 450, target_vol: 700, duration: 20 } + - { waveform: noise_tunable, target_freq: 300, target_vol: 200, duration: 40 } + - { waveform: noise_tunable, target_freq: 200, target_vol: 0, duration: 35 } + - note: 79 + _comment: Open Cuica + start_freq: 500 + start_vol: 950 + steps: + - { waveform: noise_tunable, target_freq: 900, target_vol: 800, duration: 35 } + - { waveform: noise_tunable, target_freq: 700, target_vol: 500, duration: 80 } + - { waveform: noise_tunable, target_freq: 500, target_vol: 100, duration: 100 } + - { waveform: noise_tunable, target_freq: 350, target_vol: 0, duration: 80 } - note: 80 _comment: Mute Triangle start_freq: 4000 @@ -1250,3 +1328,51 @@ drum_instruments: - { waveform: sine, target_freq: 3900, target_vol: 500, duration: 90 } - { waveform: sine, target_freq: 3750, target_vol: 200, duration: 280 } - { waveform: sine, target_freq: 3600, target_vol: 0, duration: 300 } + - note: 82 + _comment: Shaker + start_freq: 5000 + start_vol: 750 + steps: + - { waveform: noise, target_freq: 5000, target_vol: 550, duration: 18 } + - { waveform: noise, target_freq: 5000, target_vol: 200, duration: 35 } + - { waveform: noise, target_freq: 5000, target_vol: 0, duration: 30 } + - note: 83 + _comment: Hi Half-Open Hi-Hat + start_freq: 8000 + start_vol: 800 + steps: + - { waveform: noise, target_freq: 8000, target_vol: 500, duration: 18 } + - { waveform: noise, target_freq: 8000, target_vol: 150, duration: 45 } + - { waveform: noise, target_freq: 8000, target_vol: 0, duration: 45 } + - note: 84 + _comment: High Hi-Hat + start_freq: 9000 + start_vol: 780 + steps: + - { waveform: noise, target_freq: 9000, target_vol: 550, duration: 40 } + - { waveform: noise, target_freq: 9000, target_vol: 180, duration: 80 } + - { waveform: noise, target_freq: 9000, target_vol: 0, duration: 90 } + - note: 85 + _comment: Crash Cymbal 3 + start_freq: 6500 + start_vol: 1000 + steps: + - { waveform: noise, target_freq: 6500, target_vol: 700, duration: 70 } + - { waveform: noise, target_freq: 6500, target_vol: 300, duration: 200 } + - { waveform: noise, target_freq: 6500, target_vol: 0, duration: 300 } + - note: 86 + _comment: Ride Cymbal 3 + start_freq: 5500 + start_vol: 720 + steps: + - { waveform: noise, target_freq: 5500, target_vol: 420, duration: 40 } + - { waveform: noise, target_freq: 5500, target_vol: 120, duration: 80 } + - { waveform: noise, target_freq: 5500, target_vol: 0, duration: 110 } + - note: 87 + _comment: Open Hi-Hat 2 + start_freq: 8000 + start_vol: 820 + steps: + - { waveform: noise, target_freq: 8000, target_vol: 650, duration: 60 } + - { waveform: noise, target_freq: 8000, target_vol: 250, duration: 130 } + - { waveform: noise, target_freq: 8000, target_vol: 0, duration: 140 } diff --git a/src/converter/instruments.py b/src/converter/instruments.py index 439d8e8..f002637 100644 --- a/src/converter/instruments.py +++ b/src/converter/instruments.py @@ -82,7 +82,9 @@ def load_instrument_params(yaml_text: str) -> InstrumentParameterMapping: logger.debug(f"Creating mappings for {len(data["melodic_instruments"])} melodic " f"instruments") for instr in data["melodic_instruments"]: - mapping.melodic_instruments[instr["instrument"]] = Instrument( + # TODO: Make people define instrument_params.yaml from 0-127 + mapping.melodic_instruments[instr["instrument"] - 1] = Instrument( + waveform=waveform_from_str(instr["waveform"]), # Each note can range from 0-63, so we'll have two tracks, each with the # same two instruments but at different octave offsets diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index b310d2b..736963d 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -1,11 +1,12 @@ import logging +from copy import deepcopy from dataclasses import dataclass from enum import IntEnum from typing import Dict, List, Tuple from mido import Message, MidiFile, tick2second -from arcade.music_types import Song +from arcade.music_types import Envelope, Instrument, Song, Track from converter.instruments import InstrumentParameterMapping from utils.logger import create_logger @@ -366,20 +367,110 @@ def find_all_melodic_instruments(timeline: List[AbsoluteCompleteNote]) -> List[i """ logger.debug("Finding all melodic instruments in the timeline") - return list(set([m.instrument for m in timeline if not m.is_drum])) + return list(sorted(set([m.instrument for m in timeline if not m.is_drum]))) -def find_is_drum_used(timeline: List[AbsoluteCompleteNote]) -> bool: +def find_all_drum_notes_used(timeline: List[AbsoluteCompleteNote]) -> List[int]: """ - Search the timeline if any drum instruments are used. Since we only support the - default drum kit, it's a boolean result. + Search the timeline for all unique drum notes. :param timeline: A list of `AbsoluteTimeMessage` objects. - :return: A bool on whether a drum instrument is used in the song. + :return: A list of ints, representing what general MIDI drum notes are in the song. """ - logger.debug("Finding if any drum instruments are used in the timeline") + logger.debug("Finding all drum notes in the timeline") - return any([m.is_drum for m in timeline]) + return list(sorted(set([m.note for m in timeline if m.is_drum]))) + + +@dataclass +class MIDIInstrumentMappingToTrackIDs: + # Map from MIDI instrument number to track IDs + # First in the tuple is the low track (MIDI notes 0-63, octave=2), + # second is the high track (MIDI notes 64-127, octave=7) + melodic_tracks: Dict[int, Tuple[int, int]] + # Track index for the drum track + drum_track: int + # Map from MIDI drum notes to DrumInstrument indicies in the track's drums + drum_notes: Dict[int, int] + + +def assemble_song(melodics_used: List[int], drums_used: List[int], + mapping: InstrumentParameterMapping) -> Tuple[ + Song, MIDIInstrumentMappingToTrackIDs]: + """ + Given a list of melodic instruments and whether the drum was used or not, assemble + an empty MakeCode Arcade song with the appropriate tracks, using the instrument + parameter mapping. + + :param melodics_used: A list of general MIDI melodic instrument indicies (ints) that + are used in the song. + :param drums_used: A list of general MIDI drum notes (ints) that are used in the + song. + :param mapping: The `InstrumentParameterMapping` object, loaded from + `load_instrument_params`. + :return: MakeCode Arcade `Song` object with the correct tracks loaded. + """ + logger.debug(f"Assembling song with {len(melodics_used)} melodic instruments and " + f"{len(drums_used)} drum notes") + + song = Song( # TODO FIGURE OUT GOOD SETTINGS FOR THESE THREE + measures=0, + beats_per_measure=4, + beats_per_minute=120, + ticks_per_beat=8, + tracks=[] + ) + id_map = MIDIInstrumentMappingToTrackIDs(melodic_tracks={}, drum_track=-1, + drum_notes={}) + next_track_id = 0 + + for melodic in melodics_used: + id_map.melodic_tracks[melodic] = (next_track_id, next_track_id + 1) + + low_track = Track( + id=next_track_id, + instrument=deepcopy(mapping.melodic_instruments[melodic]), + notes=[], + name=f"MIDI instrument {melodic} low track", + ) + low_track.instrument.octave = 2 + next_track_id += 1 + + high_track = Track( + id=next_track_id, + instrument=deepcopy(mapping.melodic_instruments[melodic]), + notes=[], + name=f"MIDI instrument {melodic} high track", + ) + high_track.instrument.octave = 7 + next_track_id += 1 + + song.tracks.append(low_track) + song.tracks.append(high_track) + + if len(drums_used) > 0: + id_map.drum_track = next_track_id + song.tracks.append(Track( + id=next_track_id, + drums=[], + instrument=Instrument( + waveform=11, + octave=4, + amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=100, + amplitude=1024) + ), + name="MIDI drum track", + notes=[] + )) + + next_drum_index = 0 + for drum in drums_used: + id_map.drum_notes[drum] = next_drum_index + # noinspection PyUnresolvedReferences + song.tracks[-1].drums.append(mapping.drum_instruments[drum]) + next_drum_index += 1 + + return song, id_map def convert_midi_to_song(midi_song: MidiFile, @@ -393,6 +484,7 @@ def convert_midi_to_song(midi_song: MidiFile, :return: MakeCode Arcade `Song` object. """ logger.debug("Converting MIDI file into MakeCode Arcade song") + global_timeline: List[AbsoluteTimeMessage] = timeline_build(midi_song) global_timeline: List[ AbsoluteTimeMessageWithInstrument] = timeline_find_instrument_data( @@ -401,8 +493,10 @@ def convert_midi_to_song(midi_song: MidiFile, global_timeline) melodics_used = find_all_melodic_instruments(global_timeline) - drum_used = find_is_drum_used(global_timeline) + drums_used = find_all_drum_notes_used(global_timeline) logger.debug(f"Song used {len(melodics_used)} melodic instruments and " - f"{"used" if drum_used else "did not use"} the drum instrument") + f"{len(drums_used)} unique drum notes") + + song, track_id_map = assemble_song(melodics_used, drums_used, mapping) - pass + return song From 07b5559b81df107de1a15998f5f0fe81e56f7a7a Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sat, 30 May 2026 23:07:17 -0400 Subject: [PATCH 11/22] Update instrument_params_2.yaml with GM2 drum notes, start preprocessing of the timeline to handle pitch and timing restrictions --- example/instrument_params.yaml | 256 +++--- example/instrument_params_2.yaml | 256 +++--- example/instrument_params_3.yaml | 1423 ++++++++++++++++++++++++++++++ src/converter/instruments.py | 4 +- src/converter/midi_to_song.py | 298 +++++-- 5 files changed, 1887 insertions(+), 350 deletions(-) create mode 100644 example/instrument_params_3.yaml diff --git a/example/instrument_params.yaml b/example/instrument_params.yaml index 74d6e30..1cf974c 100644 --- a/example/instrument_params.yaml +++ b/example/instrument_params.yaml @@ -35,896 +35,896 @@ https://arcade.makecode.com/developer/sound > long cycles being noisier. " melodic_instruments: - - instrument: 1 + - instrument: 0 _comment: Acoustic Grand Piano waveform: triangle amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 2 + - instrument: 1 _comment: Bright Acoustic Piano waveform: triangle amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 3 + - instrument: 2 _comment: Electric Grand Piano waveform: triangle amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 4 + - instrument: 3 _comment: Honky-tonk Piano waveform: sawtooth amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 5 + - instrument: 4 _comment: Electric Piano 1 waveform: triangle amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 6 + - instrument: 5 _comment: Electric Piano 2 waveform: triangle amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 7 + - instrument: 6 _comment: Harpsichord waveform: square_20 amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 8 + - instrument: 7 _comment: Clavi waveform: sawtooth amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 9 + - instrument: 8 _comment: Celesta waveform: sine amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 10 + - instrument: 9 _comment: Glockenspiel waveform: sine amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 11 + - instrument: 10 _comment: Music Box waveform: sine amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 12 + - instrument: 11 _comment: Vibraphone waveform: sine amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 13 + - instrument: 12 _comment: Marimba waveform: triangle amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 14 + - instrument: 13 _comment: Xylophone waveform: triangle amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 15 + - instrument: 14 _comment: Tubular Bells waveform: triangle amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 16 + - instrument: 15 _comment: Dulcimer waveform: triangle amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 17 + - instrument: 16 _comment: Drawbar Organ waveform: square_50 amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 6, amplitude: 15 } - - instrument: 18 + - instrument: 17 _comment: Percussive Organ waveform: square_50 amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 19 + - instrument: 18 _comment: Rock Organ waveform: square_50 amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 6, amplitude: 15 } - - instrument: 20 + - instrument: 19 _comment: Church Organ waveform: square_50 amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 21 + - instrument: 20 _comment: Reed Organ waveform: sawtooth amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 22 + - instrument: 21 _comment: Accordion waveform: sawtooth amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 23 + - instrument: 22 _comment: Harmonica waveform: sawtooth amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 24 + - instrument: 23 _comment: Tango Accordion waveform: sawtooth amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 25 + - instrument: 24 _comment: Acoustic Guitar (nylon) waveform: triangle amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 26 + - instrument: 25 _comment: Acoustic Guitar (steel) waveform: triangle amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 27 + - instrument: 26 _comment: Electric Guitar (jazz) waveform: triangle amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 28 + - instrument: 27 _comment: Electric Guitar (clean) waveform: triangle amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 29 + - instrument: 28 _comment: Electric Guitar (muted) waveform: square_30 amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 30 + - instrument: 29 _comment: Overdriven Guitar waveform: sawtooth amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 31 + - instrument: 30 _comment: Distortion Guitar waveform: sawtooth amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 32 + - instrument: 31 _comment: Guitar harmonics waveform: triangle amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 33 + - instrument: 32 _comment: Acoustic Bass waveform: triangle amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 34 + - instrument: 33 _comment: Electric Bass (finger) waveform: triangle amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 35 + - instrument: 34 _comment: Electric Bass (pick) waveform: triangle amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 36 + - instrument: 35 _comment: Fretless Bass waveform: triangle amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 37 + - instrument: 36 _comment: Slap Bass 1 waveform: square_10 amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 38 + - instrument: 37 _comment: Slap Bass 2 waveform: square_10 amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 39 + - instrument: 38 _comment: Synth Bass 1 waveform: square_10 amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 40 + - instrument: 39 _comment: Synth Bass 2 waveform: square_10 amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 41 + - instrument: 40 _comment: Violin waveform: sawtooth amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 42 + - instrument: 41 _comment: Viola waveform: sawtooth amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 43 + - instrument: 42 _comment: Cello waveform: sawtooth amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 44 + - instrument: 43 _comment: Contrabass waveform: sawtooth amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 45 + - instrument: 44 _comment: Tremolo Strings waveform: sawtooth amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 46 + - instrument: 45 _comment: Pizzicato Strings waveform: triangle amp_envelope: { attack: 2, decay: 100, sustain: 0, release: 50, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 47 + - instrument: 46 _comment: Orchestral Harp waveform: sawtooth amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 48 + - instrument: 47 _comment: Timpani waveform: triangle amp_envelope: { attack: 5, decay: 400, sustain: 0, release: 300, amplitude: 1000 } pitch_envelope: { attack: 0, decay: 150, sustain: 0, release: 0, amplitude: -50 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 49 + - instrument: 48 _comment: String Ensemble 1 waveform: sawtooth amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 50 + - instrument: 49 _comment: String Ensemble 2 waveform: sawtooth amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 51 + - instrument: 50 _comment: SynthStrings 1 waveform: sawtooth amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 52 + - instrument: 51 _comment: SynthStrings 2 waveform: sawtooth amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 53 + - instrument: 52 _comment: Choir Aahs waveform: sawtooth amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 54 + - instrument: 53 _comment: Voice Oohs waveform: sawtooth amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 55 + - instrument: 54 _comment: Synth Voice waveform: sawtooth amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 56 + - instrument: 55 _comment: Orchestra Hit waveform: sawtooth amp_envelope: { attack: 5, decay: 150, sustain: 100, release: 100, amplitude: 1024 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 57 + - instrument: 56 _comment: Trumpet waveform: sawtooth amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 58 + - instrument: 57 _comment: Trombone waveform: sawtooth amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 59 + - instrument: 58 _comment: Tuba waveform: sawtooth amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 60 + - instrument: 59 _comment: Muted Trumpet waveform: square_40 amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 61 + - instrument: 60 _comment: French Horn waveform: sawtooth amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 62 + - instrument: 61 _comment: Brass Section waveform: sawtooth amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 63 + - instrument: 62 _comment: SynthBrass 1 waveform: square_40 amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 64 + - instrument: 63 _comment: SynthBrass 2 waveform: square_40 amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 65 + - instrument: 64 _comment: Soprano Sax waveform: square_50 amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 66 + - instrument: 65 _comment: Alto Sax waveform: square_50 amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 67 + - instrument: 66 _comment: Tenor Sax waveform: square_50 amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 68 + - instrument: 67 _comment: Baritone Sax waveform: square_50 amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 69 + - instrument: 68 _comment: Oboe waveform: square_50 amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 70 + - instrument: 69 _comment: English Horn waveform: square_50 amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 71 + - instrument: 70 _comment: Bassoon waveform: square_50 amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 72 + - instrument: 71 _comment: Clarinet waveform: square_50 amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 73 + - instrument: 72 _comment: Piccolo waveform: sine amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 74 + - instrument: 73 _comment: Flute waveform: sine amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 75 + - instrument: 74 _comment: Recorder waveform: sine amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 76 + - instrument: 75 _comment: Pan Flute waveform: triangle amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 77 + - instrument: 76 _comment: Blown Bottle waveform: triangle amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 78 + - instrument: 77 _comment: Shakuhachi waveform: triangle amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 79 + - instrument: 78 _comment: Whistle waveform: triangle amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 80 + - instrument: 79 _comment: Ocarina waveform: triangle amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 81 + - instrument: 80 _comment: Lead 1 (square) waveform: square_50 amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 82 + - instrument: 81 _comment: Lead 2 (sawtooth) waveform: sawtooth amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 83 + - instrument: 82 _comment: Lead 3 (calliope) waveform: square_50 amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 84 + - instrument: 83 _comment: Lead 4 (chiff) waveform: sawtooth amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 85 + - instrument: 84 _comment: Lead 5 (charang) waveform: sawtooth amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 86 + - instrument: 85 _comment: Lead 6 (voice) waveform: sawtooth amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 87 + - instrument: 86 _comment: Lead 7 (fifths) waveform: sawtooth amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 88 + - instrument: 87 _comment: Lead 8 (bass + lead) waveform: sawtooth amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 89 + - instrument: 88 _comment: Pad 1 (new age) waveform: triangle amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 90 + - instrument: 89 _comment: Pad 2 (warm) waveform: sine amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 91 + - instrument: 90 _comment: Pad 3 (polysynth) waveform: triangle amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 92 + - instrument: 91 _comment: Pad 4 (choir) waveform: sine amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 93 + - instrument: 92 _comment: Pad 5 (bowed) waveform: triangle amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 94 + - instrument: 93 _comment: Pad 6 (metallic) waveform: triangle amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 95 + - instrument: 94 _comment: Pad 7 (halo) waveform: triangle amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 96 + - instrument: 95 _comment: Pad 8 (sweep) waveform: triangle amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 97 + - instrument: 96 _comment: FX 1 (rain) waveform: cycle_32 amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 98 + - instrument: 97 _comment: FX 2 (soundtrack) waveform: sawtooth amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 99 + - instrument: 98 _comment: FX 3 (crystal) waveform: sawtooth amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 100 + - instrument: 99 _comment: FX 4 (atmosphere) waveform: sawtooth amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 101 + - instrument: 100 _comment: FX 5 (brightness) waveform: sawtooth amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 102 + - instrument: 101 _comment: FX 6 (goblins) waveform: cycle_32 amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 103 + - instrument: 102 _comment: FX 7 (echoes) waveform: sawtooth amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 104 + - instrument: 103 _comment: FX 8 (sci-fi) waveform: sawtooth amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 105 + - instrument: 104 _comment: Sitar waveform: sawtooth amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 106 + - instrument: 105 _comment: Banjo waveform: triangle amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 107 + - instrument: 106 _comment: Shamisen waveform: triangle amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 108 + - instrument: 107 _comment: Koto waveform: triangle amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 109 + - instrument: 108 _comment: Kalimba waveform: triangle amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 110 + - instrument: 109 _comment: Bagpipe waveform: sawtooth amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 111 + - instrument: 110 _comment: Fiddle waveform: sawtooth amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 112 + - instrument: 111 _comment: Shanai waveform: triangle amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 113 + - instrument: 112 _comment: Tinkle Bell waveform: sine amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 114 + - instrument: 113 _comment: Agogo waveform: triangle amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 115 + - instrument: 114 _comment: Steel Drums waveform: sine amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 116 + - instrument: 115 _comment: Woodblock waveform: triangle amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 117 + - instrument: 116 _comment: Taiko Drum waveform: cycle_16 amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 118 + - instrument: 117 _comment: Melodic Tom waveform: cycle_16 amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 119 + - instrument: 118 _comment: Synth Drum waveform: triangle amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 120 + - instrument: 119 _comment: Reverse Cymbal waveform: triangle amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 121 + - instrument: 120 _comment: Guitar Fret Noise waveform: noise_tunable amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 122 + - instrument: 121 _comment: Breath Noise waveform: noise_tunable amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 123 + - instrument: 122 _comment: Seashore waveform: noise amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 124 + - instrument: 123 _comment: Bird Tweet waveform: noise_tunable amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 125 + - instrument: 124 _comment: Telephone Ring waveform: noise_tunable amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 126 + - instrument: 125 _comment: Helicopter waveform: noise amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 127 + - instrument: 126 _comment: Applause waveform: noise amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 128 + - instrument: 127 _comment: Gunshot waveform: noise amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } diff --git a/example/instrument_params_2.yaml b/example/instrument_params_2.yaml index a2f77a4..17c7e61 100644 --- a/example/instrument_params_2.yaml +++ b/example/instrument_params_2.yaml @@ -40,896 +40,896 @@ _comment2: 'MakeCode Arcade GM1/GM2 instrument parameter map. All envelope times noise, low=crackling). cycle_N are periodic pseudo-random patterns (shorter = more regular). Drum notes: GM2 extended range 27-87 (GM1 was 35-81). Notes 71-74 and 78-79 are unassigned in both GM1 and GM2.' melodic_instruments: - - instrument: 1 + - instrument: 0 _comment: Acoustic Grand Piano waveform: triangle amp_envelope: { attack: 5, decay: 400, sustain: 300, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 2 + - instrument: 1 _comment: Bright Acoustic Piano waveform: triangle amp_envelope: { attack: 5, decay: 280, sustain: 250, release: 150, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 3 + - instrument: 2 _comment: Electric Grand Piano waveform: triangle amp_envelope: { attack: 5, decay: 350, sustain: 350, release: 180, amplitude: 880 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 4 + - instrument: 3 _comment: Honky-tonk Piano waveform: triangle amp_envelope: { attack: 5, decay: 250, sustain: 250, release: 100, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 6 } - - instrument: 5 + - instrument: 4 _comment: Electric Piano 1 waveform: triangle amp_envelope: { attack: 8, decay: 500, sustain: 300, release: 250, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 6 + - instrument: 5 _comment: Electric Piano 2 waveform: sine amp_envelope: { attack: 5, decay: 400, sustain: 400, release: 200, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 7 + - instrument: 6 _comment: Harpsichord waveform: sawtooth amp_envelope: { attack: 2, decay: 400, sustain: 0, release: 50, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 8 + - instrument: 7 _comment: Clavinet waveform: sawtooth amp_envelope: { attack: 2, decay: 180, sustain: 0, release: 60, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 9 + - instrument: 8 _comment: Celesta waveform: sine amp_envelope: { attack: 5, decay: 900, sustain: 0, release: 200, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 10 + - instrument: 9 _comment: Glockenspiel waveform: sine amp_envelope: { attack: 2, decay: 1100, sustain: 0, release: 300, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 11 + - instrument: 10 _comment: Music Box waveform: sine amp_envelope: { attack: 2, decay: 700, sustain: 0, release: 150, amplitude: 750 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 12 + - instrument: 11 _comment: Vibraphone waveform: sine amp_envelope: { attack: 10, decay: 900, sustain: 50, release: 500, amplitude: 830 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 5, amplitude: 50 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 13 + - instrument: 12 _comment: Marimba waveform: triangle amp_envelope: { attack: 2, decay: 350, sustain: 0, release: 80, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 14 + - instrument: 13 _comment: Xylophone waveform: triangle amp_envelope: { attack: 2, decay: 280, sustain: 0, release: 70, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 15 + - instrument: 14 _comment: Tubular Bells waveform: sine amp_envelope: { attack: 5, decay: 1400, sustain: 30, release: 500, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 16 + - instrument: 15 _comment: Dulcimer waveform: triangle amp_envelope: { attack: 5, decay: 500, sustain: 80, release: 180, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 17 + - instrument: 16 _comment: Drawbar Organ waveform: square_50 amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 3 } - - instrument: 18 + - instrument: 17 _comment: Percussive Organ waveform: square_50 amp_envelope: { attack: 5, decay: 180, sustain: 700, release: 40, amplitude: 880 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 19 + - instrument: 18 _comment: Rock Organ waveform: square_50 amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 20 + - instrument: 19 _comment: Church Organ waveform: square_50 amp_envelope: { attack: 30, decay: 0, sustain: 900, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 21 + - instrument: 20 _comment: Reed Organ waveform: square_30 amp_envelope: { attack: 20, decay: 0, sustain: 850, release: 80, amplitude: 880 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 22 + - instrument: 21 _comment: Accordion waveform: square_30 amp_envelope: { attack: 15, decay: 0, sustain: 850, release: 60, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 5, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 23 + - instrument: 22 _comment: Harmonica waveform: square_20 amp_envelope: { attack: 15, decay: 50, sustain: 800, release: 80, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 5, amplitude: 40 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 24 + - instrument: 23 _comment: Tango Accordion waveform: square_30 amp_envelope: { attack: 10, decay: 50, sustain: 850, release: 60, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 6, amplitude: 30 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 25 + - instrument: 24 _comment: Acoustic Guitar (nylon) waveform: triangle amp_envelope: { attack: 5, decay: 380, sustain: 80, release: 280, amplitude: 830 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 26 + - instrument: 25 _comment: Acoustic Guitar (steel) waveform: triangle amp_envelope: { attack: 3, decay: 320, sustain: 120, release: 220, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 27 + - instrument: 26 _comment: Electric Guitar (jazz) waveform: triangle amp_envelope: { attack: 5, decay: 380, sustain: 300, release: 180, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 28 + - instrument: 27 _comment: Electric Guitar (clean) waveform: triangle amp_envelope: { attack: 3, decay: 280, sustain: 400, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 29 + - instrument: 28 _comment: Electric Guitar (muted) waveform: triangle amp_envelope: { attack: 2, decay: 120, sustain: 0, release: 60, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 30 + - instrument: 29 _comment: Overdriven Guitar waveform: cycle_16 amp_envelope: { attack: 3, decay: 180, sustain: 700, release: 120, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 31 + - instrument: 30 _comment: Distortion Guitar waveform: cycle_32 amp_envelope: { attack: 2, decay: 100, sustain: 800, release: 100, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 32 + - instrument: 31 _comment: Guitar Harmonics waveform: sine amp_envelope: { attack: 5, decay: 700, sustain: 150, release: 300, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 33 + - instrument: 32 _comment: Acoustic Bass waveform: triangle amp_envelope: { attack: 5, decay: 280, sustain: 450, release: 130, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 34 + - instrument: 33 _comment: Electric Bass (finger) waveform: triangle amp_envelope: { attack: 5, decay: 230, sustain: 520, release: 120, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 35 + - instrument: 34 _comment: Electric Bass (pick) waveform: sawtooth amp_envelope: { attack: 2, decay: 180, sustain: 520, release: 90, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 36 + - instrument: 35 _comment: Fretless Bass waveform: triangle amp_envelope: { attack: 10, decay: 200, sustain: 600, release: 200, amplitude: 880 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 3, amplitude: 6 } - - instrument: 37 + - instrument: 36 _comment: Slap Bass 1 waveform: triangle amp_envelope: { attack: 2, decay: 140, sustain: 300, release: 90, amplitude: 940 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 38 + - instrument: 37 _comment: Slap Bass 2 waveform: sawtooth amp_envelope: { attack: 2, decay: 140, sustain: 350, release: 90, amplitude: 950 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 39 + - instrument: 38 _comment: Synth Bass 1 waveform: square_50 amp_envelope: { attack: 5, decay: 180, sustain: 620, release: 90, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 40 + - instrument: 39 _comment: Synth Bass 2 waveform: sawtooth amp_envelope: { attack: 5, decay: 180, sustain: 620, release: 90, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 41 + - instrument: 40 _comment: Violin waveform: sawtooth amp_envelope: { attack: 50, decay: 0, sustain: 900, release: 150, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 9 } - - instrument: 42 + - instrument: 41 _comment: Viola waveform: sawtooth amp_envelope: { attack: 60, decay: 0, sustain: 880, release: 150, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 8 } - - instrument: 43 + - instrument: 42 _comment: Cello waveform: sawtooth amp_envelope: { attack: 70, decay: 0, sustain: 880, release: 200, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 8 } - - instrument: 44 + - instrument: 43 _comment: Contrabass waveform: sawtooth amp_envelope: { attack: 80, decay: 0, sustain: 870, release: 200, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 3, amplitude: 7 } - - instrument: 45 + - instrument: 44 _comment: Tremolo Strings waveform: sawtooth amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 150, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 8, amplitude: 100 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 46 + - instrument: 45 _comment: Pizzicato Strings waveform: triangle amp_envelope: { attack: 3, decay: 380, sustain: 0, release: 130, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 47 + - instrument: 46 _comment: Orchestral Harp waveform: triangle amp_envelope: { attack: 5, decay: 480, sustain: 80, release: 280, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 48 + - instrument: 47 _comment: Timpani waveform: triangle amp_envelope: { attack: 3, decay: 600, sustain: 40, release: 280, amplitude: 900 } pitch_envelope: { attack: 5, decay: 300, sustain: 0, release: 150, amplitude: 120 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 49 + - instrument: 48 _comment: String Ensemble 1 waveform: sawtooth amp_envelope: { attack: 80, decay: 0, sustain: 880, release: 200, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 10 } - - instrument: 50 + - instrument: 49 _comment: String Ensemble 2 waveform: sawtooth amp_envelope: { attack: 100, decay: 0, sustain: 860, release: 250, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 8 } - - instrument: 51 + - instrument: 50 _comment: Synth Strings 1 waveform: sawtooth amp_envelope: { attack: 60, decay: 100, sustain: 800, release: 300, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 52 + - instrument: 51 _comment: Synth Strings 2 waveform: square_40 amp_envelope: { attack: 80, decay: 100, sustain: 780, release: 300, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 53 + - instrument: 52 _comment: Choir Aahs waveform: sine amp_envelope: { attack: 120, decay: 50, sustain: 900, release: 300, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 6 } - - instrument: 54 + - instrument: 53 _comment: Voice Oohs waveform: sine amp_envelope: { attack: 140, decay: 50, sustain: 900, release: 300, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 5 } - - instrument: 55 + - instrument: 54 _comment: Synth Voice waveform: square_20 amp_envelope: { attack: 90, decay: 50, sustain: 870, release: 250, amplitude: 830 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 7 } - - instrument: 56 + - instrument: 55 _comment: Orchestra Hit waveform: sawtooth amp_envelope: { attack: 2, decay: 300, sustain: 0, release: 100, amplitude: 960 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 57 + - instrument: 56 _comment: Trumpet waveform: sawtooth amp_envelope: { attack: 20, decay: 50, sustain: 900, release: 100, amplitude: 930 } pitch_envelope: { attack: 12, decay: 60, sustain: 0, release: 50, amplitude: 40 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 58 + - instrument: 57 _comment: Trombone waveform: sawtooth amp_envelope: { attack: 30, decay: 50, sustain: 880, release: 150, amplitude: 920 } pitch_envelope: { attack: 15, decay: 60, sustain: 0, release: 60, amplitude: 35 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 59 + - instrument: 58 _comment: Tuba waveform: sawtooth amp_envelope: { attack: 30, decay: 50, sustain: 870, release: 150, amplitude: 910 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 60 + - instrument: 59 _comment: Muted Trumpet waveform: square_20 amp_envelope: { attack: 15, decay: 50, sustain: 800, release: 80, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 61 + - instrument: 60 _comment: French Horn waveform: sawtooth amp_envelope: { attack: 55, decay: 50, sustain: 880, release: 200, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 6 } - - instrument: 62 + - instrument: 61 _comment: Brass Section waveform: sawtooth amp_envelope: { attack: 15, decay: 50, sustain: 900, release: 100, amplitude: 940 } pitch_envelope: { attack: 10, decay: 50, sustain: 0, release: 40, amplitude: 30 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 63 + - instrument: 62 _comment: Synth Brass 1 waveform: square_50 amp_envelope: { attack: 10, decay: 100, sustain: 850, release: 150, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 64 + - instrument: 63 _comment: Synth Brass 2 waveform: sawtooth amp_envelope: { attack: 10, decay: 100, sustain: 850, release: 150, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 65 + - instrument: 64 _comment: Soprano Sax waveform: square_30 amp_envelope: { attack: 20, decay: 50, sustain: 850, release: 100, amplitude: 890 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 9 } - - instrument: 66 + - instrument: 65 _comment: Alto Sax waveform: square_30 amp_envelope: { attack: 20, decay: 50, sustain: 850, release: 100, amplitude: 880 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 9 } - - instrument: 67 + - instrument: 66 _comment: Tenor Sax waveform: square_30 amp_envelope: { attack: 25, decay: 50, sustain: 840, release: 120, amplitude: 880 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 8 } - - instrument: 68 + - instrument: 67 _comment: Baritone Sax waveform: square_30 amp_envelope: { attack: 30, decay: 50, sustain: 830, release: 150, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 7 } - - instrument: 69 + - instrument: 68 _comment: Oboe waveform: square_10 amp_envelope: { attack: 20, decay: 30, sustain: 870, release: 80, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 7 } - - instrument: 70 + - instrument: 69 _comment: English Horn waveform: square_20 amp_envelope: { attack: 25, decay: 30, sustain: 860, release: 100, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 6 } - - instrument: 71 + - instrument: 70 _comment: Bassoon waveform: square_20 amp_envelope: { attack: 30, decay: 30, sustain: 850, release: 120, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 5 } - - instrument: 72 + - instrument: 71 _comment: Clarinet waveform: square_50 amp_envelope: { attack: 20, decay: 30, sustain: 870, release: 100, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 7 } - - instrument: 73 + - instrument: 72 _comment: Piccolo waveform: triangle amp_envelope: { attack: 15, decay: 20, sustain: 880, release: 80, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 6 } - - instrument: 74 + - instrument: 73 _comment: Flute waveform: sine amp_envelope: { attack: 20, decay: 20, sustain: 880, release: 100, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 5, amplitude: 20 } pitch_lfo: { frequency: 5, amplitude: 9 } - - instrument: 75 + - instrument: 74 _comment: Recorder waveform: triangle amp_envelope: { attack: 15, decay: 20, sustain: 870, release: 80, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 6 } - - instrument: 76 + - instrument: 75 _comment: Pan Flute waveform: sine amp_envelope: { attack: 35, decay: 20, sustain: 870, release: 120, amplitude: 830 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 8 } - - instrument: 77 + - instrument: 76 _comment: Blown Bottle waveform: noise_tunable amp_envelope: { attack: 25, decay: 120, sustain: 650, release: 180, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 78 + - instrument: 77 _comment: Shakuhachi waveform: noise_tunable amp_envelope: { attack: 60, decay: 60, sustain: 750, release: 220, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 12 } - - instrument: 79 + - instrument: 78 _comment: Whistle waveform: sine amp_envelope: { attack: 15, decay: 0, sustain: 900, release: 80, amplitude: 830 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 10 } - - instrument: 80 + - instrument: 79 _comment: Ocarina waveform: sine amp_envelope: { attack: 20, decay: 20, sustain: 880, release: 100, amplitude: 830 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 8 } - - instrument: 81 + - instrument: 80 _comment: Lead 1 (square) waveform: square_50 amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 82 + - instrument: 81 _comment: Lead 2 (sawtooth) waveform: sawtooth amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 83 + - instrument: 82 _comment: Lead 3 (calliope) waveform: triangle amp_envelope: { attack: 10, decay: 50, sustain: 800, release: 100, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 84 + - instrument: 83 _comment: Lead 4 (chiff) waveform: cycle_16 amp_envelope: { attack: 5, decay: 120, sustain: 700, release: 100, amplitude: 880 } pitch_envelope: { attack: 6, decay: 80, sustain: 0, release: 50, amplitude: 30 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 85 + - instrument: 84 _comment: Lead 5 (charang) waveform: cycle_32 amp_envelope: { attack: 5, decay: 100, sustain: 800, release: 100, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 3, amplitude: 6 } - - instrument: 86 + - instrument: 85 _comment: Lead 6 (voice) waveform: square_20 amp_envelope: { attack: 30, decay: 50, sustain: 850, release: 150, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 8 } - - instrument: 87 + - instrument: 86 _comment: Lead 7 (fifths) waveform: square_50 amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 910 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 88 + - instrument: 87 _comment: Lead 8 (bass+lead) waveform: sawtooth amp_envelope: { attack: 5, decay: 100, sustain: 800, release: 100, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 89 + - instrument: 88 _comment: Pad 1 (new age) waveform: sine amp_envelope: { attack: 220, decay: 100, sustain: 800, release: 450, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 90 + - instrument: 89 _comment: Pad 2 (warm) waveform: triangle amp_envelope: { attack: 170, decay: 100, sustain: 850, release: 450, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 91 + - instrument: 90 _comment: Pad 3 (polysynth) waveform: sawtooth amp_envelope: { attack: 110, decay: 100, sustain: 800, release: 380, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 92 + - instrument: 91 _comment: Pad 4 (choir) waveform: sine amp_envelope: { attack: 160, decay: 50, sustain: 900, release: 430, amplitude: 830 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 3, amplitude: 6 } - - instrument: 93 + - instrument: 92 _comment: Pad 5 (bowed) waveform: triangle amp_envelope: { attack: 220, decay: 0, sustain: 900, release: 520, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 5 } - - instrument: 94 + - instrument: 93 _comment: Pad 6 (metallic) waveform: cycle_16 amp_envelope: { attack: 110, decay: 200, sustain: 700, release: 420, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 95 + - instrument: 94 _comment: Pad 7 (halo) waveform: sine amp_envelope: { attack: 270, decay: 100, sustain: 800, release: 520, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 3, amplitude: 9 } - - instrument: 96 + - instrument: 95 _comment: Pad 8 (sweep) waveform: sawtooth amp_envelope: { attack: 220, decay: 200, sustain: 700, release: 520, amplitude: 830 } pitch_envelope: { attack: 60, decay: 350, sustain: 0, release: 220, amplitude: 60 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 97 + - instrument: 96 _comment: FX 1 (rain) waveform: noise_tunable amp_envelope: { attack: 55, decay: 200, sustain: 500, release: 420, amplitude: 800 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 98 + - instrument: 97 _comment: FX 2 (soundtrack) waveform: sawtooth amp_envelope: { attack: 110, decay: 100, sustain: 800, release: 420, amplitude: 830 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 99 + - instrument: 98 _comment: FX 3 (crystal) waveform: sine amp_envelope: { attack: 20, decay: 550, sustain: 80, release: 420, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 100 + - instrument: 99 _comment: FX 4 (atmosphere) waveform: triangle amp_envelope: { attack: 160, decay: 200, sustain: 700, release: 520, amplitude: 810 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 101 + - instrument: 100 _comment: FX 5 (brightness) waveform: sawtooth amp_envelope: { attack: 30, decay: 100, sustain: 800, release: 320, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 102 + - instrument: 101 _comment: FX 6 (goblins) waveform: cycle_32 amp_envelope: { attack: 110, decay: 100, sustain: 700, release: 420, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 3, amplitude: 18 } - - instrument: 103 + - instrument: 102 _comment: FX 7 (echoes) waveform: sine amp_envelope: { attack: 55, decay: 300, sustain: 400, release: 520, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 104 + - instrument: 103 _comment: FX 8 (sci-fi) waveform: cycle_64 amp_envelope: { attack: 85, decay: 200, sustain: 600, release: 420, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 2, amplitude: 22 } - - instrument: 105 + - instrument: 104 _comment: Sitar waveform: sawtooth amp_envelope: { attack: 5, decay: 550, sustain: 180, release: 280, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 106 + - instrument: 105 _comment: Banjo waveform: triangle amp_envelope: { attack: 3, decay: 380, sustain: 80, release: 180, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 107 + - instrument: 106 _comment: Shamisen waveform: sawtooth amp_envelope: { attack: 3, decay: 320, sustain: 80, release: 130, amplitude: 880 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 108 + - instrument: 107 _comment: Koto waveform: triangle amp_envelope: { attack: 5, decay: 580, sustain: 80, release: 280, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 109 + - instrument: 108 _comment: Kalimba waveform: sine amp_envelope: { attack: 3, decay: 650, sustain: 0, release: 180, amplitude: 840 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 110 + - instrument: 109 _comment: Bag Pipe waveform: square_50 amp_envelope: { attack: 50, decay: 0, sustain: 900, release: 100, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 4, amplitude: 6 } - - instrument: 111 + - instrument: 110 _comment: Fiddle waveform: sawtooth amp_envelope: { attack: 30, decay: 0, sustain: 900, release: 150, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 9 } - - instrument: 112 + - instrument: 111 _comment: Shanai waveform: square_10 amp_envelope: { attack: 20, decay: 50, sustain: 860, release: 100, amplitude: 860 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 5, amplitude: 9 } - - instrument: 113 + - instrument: 112 _comment: Tinkle Bell waveform: sine amp_envelope: { attack: 2, decay: 850, sustain: 0, release: 180, amplitude: 820 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 114 + - instrument: 113 _comment: Agogo waveform: triangle amp_envelope: { attack: 2, decay: 480, sustain: 0, release: 130, amplitude: 870 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 115 + - instrument: 114 _comment: Steel Drums waveform: sine amp_envelope: { attack: 5, decay: 620, sustain: 80, release: 180, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 116 + - instrument: 115 _comment: Woodblock waveform: cycle_16 amp_envelope: { attack: 2, decay: 90, sustain: 0, release: 40, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 117 + - instrument: 116 _comment: Taiko Drum waveform: triangle amp_envelope: { attack: 2, decay: 380, sustain: 0, release: 180, amplitude: 920 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 118 + - instrument: 117 _comment: Melodic Tom waveform: triangle amp_envelope: { attack: 3, decay: 280, sustain: 80, release: 130, amplitude: 880 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 119 + - instrument: 118 _comment: Synth Drum waveform: square_50 amp_envelope: { attack: 3, decay: 280, sustain: 0, release: 90, amplitude: 900 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 120 + - instrument: 119 _comment: Reverse Cymbal waveform: noise amp_envelope: { attack: 900, decay: 0, sustain: 900, release: 50, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 121 + - instrument: 120 _comment: Guitar Fret Noise waveform: noise_tunable amp_envelope: { attack: 2, decay: 90, sustain: 0, release: 25, amplitude: 600 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 122 + - instrument: 121 _comment: Breath Noise waveform: noise_tunable amp_envelope: { attack: 10, decay: 50, sustain: 400, release: 100, amplitude: 600 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 123 + - instrument: 122 _comment: Seashore waveform: noise amp_envelope: { attack: 200, decay: 0, sustain: 600, release: 400, amplitude: 550 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 124 + - instrument: 123 _comment: Bird Tweet waveform: sine amp_envelope: { attack: 5, decay: 100, sustain: 700, release: 100, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 9, amplitude: 35 } - - instrument: 125 + - instrument: 124 _comment: Telephone Ring waveform: square_50 amp_envelope: { attack: 5, decay: 0, sustain: 900, release: 50, amplitude: 850 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 2, amplitude: 900 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 126 + - instrument: 125 _comment: Helicopter waveform: cycle_16 amp_envelope: { attack: 100, decay: 0, sustain: 700, release: 200, amplitude: 700 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 4, amplitude: 20 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 127 + - instrument: 126 _comment: Applause waveform: noise amp_envelope: { attack: 220, decay: 100, sustain: 600, release: 400, amplitude: 650 } pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } amp_lfo: { frequency: 0, amplitude: 0 } pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 128 + - instrument: 127 _comment: Gunshot waveform: noise amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 950 } diff --git a/example/instrument_params_3.yaml b/example/instrument_params_3.yaml new file mode 100644 index 0000000..75e44f8 --- /dev/null +++ b/example/instrument_params_3.yaml @@ -0,0 +1,1423 @@ +_comment: " +This file was generated by Claude for testing purposes. + +Units: + Attack, decay, release: milliseconds + Sustain and amplitudes: range 0-1024 + LFO frequencies: hz + +Waveforms map to as expected by MakeCode Arcade: + triangle = 1 + sawtooth = 2 + sine = 3 + noise_tunable = 4 + noise = 5 + square_10 = 11 + square_20 = 12 + square_30 = 13 + square_40 = 14 + square_50 (or square) = 15 + cycle_16 = 16 + cycle_32 = 17 + cycle_64 (or noise_tunable_2) = 18 + +https://arcade.makecode.com/developer/sound +> The noise is an actual white noise. It ignores the requested frequency or +> note. +> +> The tunable noise tone contains a pseudorandom sample. At high frequency it +> sounds similar to white noise, at low frequency it produces irregular +> crackling noises. +> +> The cycle 16, cycle 32, and cycle 64 waveforms are repeating pseudorandom +> patterns with a cycle length of 16/32/64 samples respectively. They sound +> like a noise-distorted square wave, with short cycles being more regular and +> long cycles being noisier. +" +melodic_instruments: + - instrument: 0 + _comment: Acoustic Grand Piano + waveform: triangle + amp_envelope: { attack: 5, decay: 400, sustain: 300, release: 200, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 1 + _comment: Bright Acoustic Piano + waveform: triangle + amp_envelope: { attack: 2, decay: 350, sustain: 250, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 2 + _comment: Electric Grand Piano + waveform: triangle + amp_envelope: { attack: 5, decay: 450, sustain: 350, release: 200, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 3 + _comment: Honky-tonk Piano + waveform: square_50 + amp_envelope: { attack: 3, decay: 350, sustain: 280, release: 150, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 5, amplitude: 10 } + - instrument: 4 + _comment: Electric Piano 1 + waveform: sine + amp_envelope: { attack: 8, decay: 500, sustain: 400, release: 300, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 5 + _comment: Electric Piano 2 + waveform: sine + amp_envelope: { attack: 8, decay: 480, sustain: 380, release: 280, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 6 + _comment: Harpsichord + waveform: sawtooth + amp_envelope: { attack: 1, decay: 200, sustain: 0, release: 50, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 7 + _comment: Clavinet + waveform: square_50 + amp_envelope: { attack: 1, decay: 150, sustain: 0, release: 80, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 8 + _comment: Celesta + waveform: triangle + amp_envelope: { attack: 2, decay: 300, sustain: 100, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 9 + _comment: Glockenspiel + waveform: triangle + amp_envelope: { attack: 1, decay: 350, sustain: 100, release: 150, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 10 + _comment: Music Box + waveform: triangle + amp_envelope: { attack: 2, decay: 400, sustain: 150, release: 200, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 11 + _comment: Vibraphone + waveform: sine + amp_envelope: { attack: 20, decay: 600, sustain: 400, release: 400, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 50, amplitude: 30 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 12 + _comment: Marimba + waveform: triangle + amp_envelope: { attack: 2, decay: 300, sustain: 100, release: 150, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 13 + _comment: Xylophone + waveform: triangle + amp_envelope: { attack: 1, decay: 250, sustain: 50, release: 100, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 14 + _comment: Tubular Bells + waveform: sawtooth + amp_envelope: { attack: 5, decay: 800, sustain: 500, release: 500, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 15 + _comment: Dulcimer + waveform: triangle + amp_envelope: { attack: 3, decay: 400, sustain: 250, release: 200, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 16 + _comment: Drawbar Organ + waveform: square_50 + amp_envelope: { attack: 10, decay: 0, sustain: 900, release: 20, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 17 + _comment: Percussive Organ + waveform: square_50 + amp_envelope: { attack: 1, decay: 400, sustain: 0, release: 50, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 18 + _comment: Rock Organ + waveform: square_50 + amp_envelope: { attack: 5, decay: 0, sustain: 880, release: 30, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 19 + _comment: Church Organ + waveform: square_30 + amp_envelope: { attack: 40, decay: 0, sustain: 900, release: 100, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 20 + _comment: Reed Organ + waveform: sawtooth + amp_envelope: { attack: 30, decay: 0, sustain: 850, release: 80, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 21 + _comment: Accordion + waveform: sawtooth + amp_envelope: { attack: 30, decay: 0, sustain: 860, release: 80, amplitude: 810 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 22 + _comment: Harmonica + waveform: square_30 + amp_envelope: { attack: 20, decay: 0, sustain: 840, release: 60, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 23 + _comment: Tango Accordion + waveform: sawtooth + amp_envelope: { attack: 25, decay: 0, sustain: 850, release: 70, amplitude: 810 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 24 + _comment: Acoustic Guitar (nylon) + waveform: triangle + amp_envelope: { attack: 3, decay: 400, sustain: 200, release: 250, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 25 + _comment: Acoustic Guitar (steel) + waveform: triangle + amp_envelope: { attack: 2, decay: 380, sustain: 180, release: 220, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 26 + _comment: Electric Guitar (jazz) + waveform: triangle + amp_envelope: { attack: 5, decay: 450, sustain: 300, release: 200, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 27 + _comment: Electric Guitar (clean) + waveform: square_20 + amp_envelope: { attack: 2, decay: 350, sustain: 200, release: 180, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 28 + _comment: Electric Guitar (muted) + waveform: square_10 + amp_envelope: { attack: 1, decay: 80, sustain: 0, release: 40, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 29 + _comment: Overdriven Guitar + waveform: sawtooth + amp_envelope: { attack: 5, decay: 200, sustain: 700, release: 150, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 30 + _comment: Distortion Guitar + waveform: sawtooth + amp_envelope: { attack: 3, decay: 200, sustain: 750, release: 120, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 31 + _comment: Guitar Harmonics + waveform: triangle + amp_envelope: { attack: 10, decay: 500, sustain: 300, release: 300, amplitude: 780 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 32 + _comment: Acoustic Bass + waveform: triangle + amp_envelope: { attack: 5, decay: 300, sustain: 200, release: 150, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 33 + _comment: Electric Bass (finger) + waveform: triangle + amp_envelope: { attack: 5, decay: 350, sustain: 250, release: 180, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 34 + _comment: Electric Bass (pick) + waveform: sawtooth + amp_envelope: { attack: 2, decay: 300, sustain: 200, release: 150, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 35 + _comment: Fretless Bass + waveform: triangle + amp_envelope: { attack: 10, decay: 400, sustain: 350, release: 250, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 20, amplitude: 8 } + - instrument: 36 + _comment: Slap Bass 1 + waveform: square_50 + amp_envelope: { attack: 1, decay: 200, sustain: 100, release: 100, amplitude: 930 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 37 + _comment: Slap Bass 2 + waveform: square_50 + amp_envelope: { attack: 1, decay: 200, sustain: 100, release: 100, amplitude: 940 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 38 + _comment: Synth Bass 1 + waveform: sawtooth + amp_envelope: { attack: 5, decay: 300, sustain: 400, release: 200, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 39 + _comment: Synth Bass 2 + waveform: square_30 + amp_envelope: { attack: 8, decay: 350, sustain: 450, release: 250, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 40 + _comment: Violin + waveform: sawtooth + amp_envelope: { attack: 60, decay: 0, sustain: 800, release: 100, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 50, amplitude: 15 } + - instrument: 41 + _comment: Viola + waveform: sawtooth + amp_envelope: { attack: 70, decay: 0, sustain: 800, release: 120, amplitude: 810 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 50, amplitude: 15 } + - instrument: 42 + _comment: Cello + waveform: sawtooth + amp_envelope: { attack: 80, decay: 0, sustain: 800, release: 150, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 45, amplitude: 15 } + - instrument: 43 + _comment: Contrabass + waveform: sawtooth + amp_envelope: { attack: 100, decay: 0, sustain: 800, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 40, amplitude: 10 } + - instrument: 44 + _comment: Tremolo Strings + waveform: triangle + amp_envelope: { attack: 50, decay: 0, sustain: 750, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 80, amplitude: 200 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 45 + _comment: Pizzicato Strings + waveform: triangle + amp_envelope: { attack: 1, decay: 200, sustain: 0, release: 100, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 46 + _comment: Orchestral Harp + waveform: triangle + amp_envelope: { attack: 2, decay: 500, sustain: 300, release: 300, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 47 + _comment: Timpani + waveform: noise_tunable + amp_envelope: { attack: 5, decay: 400, sustain: 200, release: 300, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 48 + _comment: String Ensemble 1 + waveform: sawtooth + amp_envelope: { attack: 80, decay: 0, sustain: 800, release: 200, amplitude: 810 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 49 + _comment: String Ensemble 2 + waveform: sawtooth + amp_envelope: { attack: 100, decay: 0, sustain: 800, release: 250, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 50 + _comment: Synth Strings 1 + waveform: sawtooth + amp_envelope: { attack: 60, decay: 0, sustain: 800, release: 200, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 30, amplitude: 10 } + - instrument: 51 + _comment: Synth Strings 2 + waveform: sawtooth + amp_envelope: { attack: 50, decay: 0, sustain: 800, release: 180, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 52 + _comment: Choir Aahs + waveform: sine + amp_envelope: { attack: 80, decay: 0, sustain: 800, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 53 + _comment: Voice Oohs + waveform: sine + amp_envelope: { attack: 100, decay: 0, sustain: 800, release: 250, amplitude: 790 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 54 + _comment: Synth Voice + waveform: sine + amp_envelope: { attack: 60, decay: 0, sustain: 800, release: 200, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 30, amplitude: 12 } + - instrument: 55 + _comment: Orchestra Hit + waveform: sawtooth + amp_envelope: { attack: 1, decay: 200, sustain: 0, release: 100, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 56 + _comment: Trumpet + waveform: sawtooth + amp_envelope: { attack: 20, decay: 0, sustain: 820, release: 80, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 57 + _comment: Trombone + waveform: sawtooth + amp_envelope: { attack: 30, decay: 0, sustain: 820, release: 100, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 58 + _comment: Tuba + waveform: sawtooth + amp_envelope: { attack: 40, decay: 0, sustain: 800, release: 120, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 59 + _comment: Muted Trumpet + waveform: square_20 + amp_envelope: { attack: 15, decay: 0, sustain: 780, release: 60, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 60 + _comment: French Horn + waveform: triangle + amp_envelope: { attack: 50, decay: 0, sustain: 800, release: 150, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 61 + _comment: Brass Section + waveform: sawtooth + amp_envelope: { attack: 25, decay: 0, sustain: 820, release: 100, amplitude: 890 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 62 + _comment: Synth Brass 1 + waveform: sawtooth + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 80, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 63 + _comment: Synth Brass 2 + waveform: square_40 + amp_envelope: { attack: 10, decay: 0, sustain: 800, release: 60, amplitude: 910 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 64 + _comment: Soprano Sax + waveform: sawtooth + amp_envelope: { attack: 15, decay: 0, sustain: 820, release: 80, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 65 + _comment: Alto Sax + waveform: sawtooth + amp_envelope: { attack: 20, decay: 0, sustain: 820, release: 90, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 66 + _comment: Tenor Sax + waveform: sawtooth + amp_envelope: { attack: 25, decay: 0, sustain: 820, release: 100, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 67 + _comment: Baritone Sax + waveform: sawtooth + amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 120, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 68 + _comment: Oboe + waveform: square_30 + amp_envelope: { attack: 20, decay: 0, sustain: 820, release: 80, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 69 + _comment: English Horn + waveform: square_30 + amp_envelope: { attack: 25, decay: 0, sustain: 820, release: 90, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 70 + _comment: Bassoon + waveform: sawtooth + amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 100, amplitude: 830 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 71 + _comment: Clarinet + waveform: square_30 + amp_envelope: { attack: 20, decay: 0, sustain: 830, release: 80, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 72 + _comment: Piccolo + waveform: triangle + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 60, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 73 + _comment: Flute + waveform: sine + amp_envelope: { attack: 20, decay: 0, sustain: 800, release: 80, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 40, amplitude: 8 } + - instrument: 74 + _comment: Recorder + waveform: triangle + amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 70, amplitude: 810 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 75 + _comment: Pan Flute + waveform: sine + amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 76 + _comment: Blown Bottle + waveform: sine + amp_envelope: { attack: 25, decay: 0, sustain: 750, release: 100, amplitude: 790 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 77 + _comment: Shakuhachi + waveform: triangle + amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 25, amplitude: 12 } + - instrument: 78 + _comment: Whistle + waveform: sine + amp_envelope: { attack: 10, decay: 0, sustain: 800, release: 60, amplitude: 810 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 79 + _comment: Ocarina + waveform: sine + amp_envelope: { attack: 20, decay: 0, sustain: 810, release: 80, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 80 + _comment: Lead 1 (square) + waveform: square_50 + amp_envelope: { attack: 5, decay: 0, sustain: 800, release: 60, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 81 + _comment: Lead 2 (sawtooth) + waveform: sawtooth + amp_envelope: { attack: 3, decay: 0, sustain: 800, release: 50, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 82 + _comment: Lead 3 (calliope) + waveform: triangle + amp_envelope: { attack: 10, decay: 0, sustain: 800, release: 80, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 83 + _comment: Lead 4 (chiff) + waveform: noise_tunable + amp_envelope: { attack: 2, decay: 300, sustain: 400, release: 200, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 84 + _comment: Lead 5 (charang) + waveform: sawtooth + amp_envelope: { attack: 5, decay: 0, sustain: 800, release: 60, amplitude: 910 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 85 + _comment: Lead 6 (voice) + waveform: sine + amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 100, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 86 + _comment: Lead 7 (fifths) + waveform: square_50 + amp_envelope: { attack: 5, decay: 0, sustain: 800, release: 60, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 87 + _comment: Lead 8 (bass+lead) + waveform: sawtooth + amp_envelope: { attack: 5, decay: 0, sustain: 800, release: 60, amplitude: 930 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 88 + _comment: Pad 1 (new age) + waveform: triangle + amp_envelope: { attack: 200, decay: 0, sustain: 800, release: 400, amplitude: 780 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 89 + _comment: Pad 2 (warm) + waveform: sine + amp_envelope: { attack: 150, decay: 0, sustain: 800, release: 350, amplitude: 770 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 90 + _comment: Pad 3 (polysynth) + waveform: sawtooth + amp_envelope: { attack: 100, decay: 0, sustain: 800, release: 300, amplitude: 790 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 91 + _comment: Pad 4 (choir) + waveform: sine + amp_envelope: { attack: 200, decay: 0, sustain: 800, release: 400, amplitude: 770 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 92 + _comment: Pad 5 (bowed) + waveform: sawtooth + amp_envelope: { attack: 180, decay: 0, sustain: 800, release: 400, amplitude: 780 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 93 + _comment: Pad 6 (metallic) + waveform: cycle_32 + amp_envelope: { attack: 100, decay: 0, sustain: 750, release: 300, amplitude: 780 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 94 + _comment: Pad 7 (halo) + waveform: triangle + amp_envelope: { attack: 200, decay: 0, sustain: 800, release: 400, amplitude: 760 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 95 + _comment: Pad 8 (sweep) + waveform: sawtooth + amp_envelope: { attack: 150, decay: 0, sustain: 800, release: 400, amplitude: 780 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 96 + _comment: FX 1 (rain) + waveform: noise_tunable + amp_envelope: { attack: 50, decay: 0, sustain: 700, release: 200, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 97 + _comment: FX 2 (soundtrack) + waveform: triangle + amp_envelope: { attack: 200, decay: 0, sustain: 800, release: 400, amplitude: 760 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 98 + _comment: FX 3 (crystal) + waveform: triangle + amp_envelope: { attack: 5, decay: 500, sustain: 300, release: 400, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 99 + _comment: FX 4 (atmosphere) + waveform: sine + amp_envelope: { attack: 200, decay: 0, sustain: 750, release: 500, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 100 + _comment: FX 5 (brightness) + waveform: triangle + amp_envelope: { attack: 10, decay: 0, sustain: 800, release: 200, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 101 + _comment: FX 6 (goblins) + waveform: cycle_32 + amp_envelope: { attack: 100, decay: 0, sustain: 750, release: 300, amplitude: 780 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 102 + _comment: FX 7 (echoes) + waveform: sine + amp_envelope: { attack: 50, decay: 400, sustain: 500, release: 500, amplitude: 760 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 103 + _comment: FX 8 (sci-fi) + waveform: cycle_64 + amp_envelope: { attack: 100, decay: 0, sustain: 750, release: 300, amplitude: 770 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 104 + _comment: Sitar + waveform: sawtooth + amp_envelope: { attack: 5, decay: 500, sustain: 350, release: 300, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 25, amplitude: 15 } + - instrument: 105 + _comment: Banjo + waveform: triangle + amp_envelope: { attack: 2, decay: 350, sustain: 150, release: 200, amplitude: 870 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 106 + _comment: Shamisen + waveform: sawtooth + amp_envelope: { attack: 2, decay: 300, sustain: 100, release: 150, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 107 + _comment: Koto + waveform: triangle + amp_envelope: { attack: 3, decay: 400, sustain: 200, release: 250, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 108 + _comment: Kalimba + waveform: triangle + amp_envelope: { attack: 1, decay: 350, sustain: 100, release: 200, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 109 + _comment: Bag Pipe + waveform: square_30 + amp_envelope: { attack: 30, decay: 0, sustain: 820, release: 80, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 110 + _comment: Fiddle + waveform: sawtooth + amp_envelope: { attack: 40, decay: 0, sustain: 800, release: 100, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 50, amplitude: 15 } + - instrument: 111 + _comment: Shanai + waveform: sawtooth + amp_envelope: { attack: 20, decay: 0, sustain: 820, release: 80, amplitude: 840 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 112 + _comment: Tinkle Bell + waveform: triangle + amp_envelope: { attack: 1, decay: 400, sustain: 200, release: 300, amplitude: 820 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 113 + _comment: Agogo + waveform: square_50 + amp_envelope: { attack: 1, decay: 200, sustain: 50, release: 100, amplitude: 880 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 114 + _comment: Steel Drums + waveform: triangle + amp_envelope: { attack: 2, decay: 350, sustain: 200, release: 250, amplitude: 860 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 115 + _comment: Woodblock + waveform: square_50 + amp_envelope: { attack: 1, decay: 80, sustain: 0, release: 40, amplitude: 920 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 116 + _comment: Taiko Drum + waveform: noise_tunable + amp_envelope: { attack: 2, decay: 300, sustain: 0, release: 150, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 117 + _comment: Melodic Tom + waveform: noise_tunable + amp_envelope: { attack: 2, decay: 350, sustain: 0, release: 200, amplitude: 930 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 118 + _comment: Synth Drum + waveform: noise + amp_envelope: { attack: 2, decay: 250, sustain: 0, release: 120, amplitude: 900 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 119 + _comment: Reverse Cymbal + waveform: noise + amp_envelope: { attack: 500, decay: 0, sustain: 800, release: 100, amplitude: 800 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 120 + _comment: Guitar Fret Noise + waveform: noise + amp_envelope: { attack: 1, decay: 100, sustain: 0, release: 50, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 121 + _comment: Breath Noise + waveform: noise + amp_envelope: { attack: 5, decay: 200, sustain: 300, release: 200, amplitude: 650 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 122 + _comment: Seashore + waveform: noise + amp_envelope: { attack: 200, decay: 0, sustain: 700, release: 400, amplitude: 650 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 123 + _comment: Bird Tweet + waveform: triangle + amp_envelope: { attack: 5, decay: 200, sustain: 400, release: 200, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 100, amplitude: 50 } + - instrument: 124 + _comment: Telephone Ring + waveform: square_50 + amp_envelope: { attack: 1, decay: 0, sustain: 800, release: 50, amplitude: 850 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 40, amplitude: 500 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 125 + _comment: Helicopter + waveform: noise_tunable + amp_envelope: { attack: 100, decay: 0, sustain: 700, release: 200, amplitude: 750 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 126 + _comment: Applause + waveform: noise + amp_envelope: { attack: 100, decay: 0, sustain: 700, release: 400, amplitude: 700 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 127 + _comment: Gunshot + waveform: noise + amp_envelope: { attack: 1, decay: 200, sustain: 0, release: 100, amplitude: 950 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + +drum_instruments: + - note: 27 + _comment: High Q + start_freq: 3500 + start_vol: 900 + steps: + - { waveform: cycle_16 , target_freq: 2800, target_vol: 500, duration: 5 } + - { waveform: cycle_16 , target_freq: 2000, target_vol: 0, duration: 8 } + - note: 28 + _comment: Slap + start_freq: 900 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 600, target_vol: 400, duration: 4 } + - { waveform: noise_tunable , target_freq: 300, target_vol: 0, duration: 6 } + - note: 29 + _comment: Scratch Push + start_freq: 300 + start_vol: 750 + steps: + - { waveform: noise_tunable , target_freq: 800, target_vol: 600, duration: 6 } + - { waveform: noise_tunable , target_freq: 1500, target_vol: 0, duration: 8 } + - note: 30 + _comment: Scratch Pull + start_freq: 1500 + start_vol: 750 + steps: + - { waveform: noise_tunable , target_freq: 800, target_vol: 500, duration: 6 } + - { waveform: noise_tunable , target_freq: 200, target_vol: 0, duration: 10 } + - note: 31 + _comment: Sticks + start_freq: 1800 + start_vol: 880 + steps: + - { waveform: square_50 , target_freq: 1600, target_vol: 400, duration: 3 } + - { waveform: square_50 , target_freq: 1200, target_vol: 0, duration: 4 } + - note: 32 + _comment: Square Click + start_freq: 1500 + start_vol: 900 + steps: + - { waveform: square_50 , target_freq: 1200, target_vol: 400, duration: 3 } + - { waveform: square_50 , target_freq: 800, target_vol: 0, duration: 4 } + - note: 33 + _comment: Metronome Click + start_freq: 2000 + start_vol: 900 + steps: + - { waveform: square_50 , target_freq: 1600, target_vol: 500, duration: 3 } + - { waveform: square_50 , target_freq: 1000, target_vol: 0, duration: 4 } + - note: 34 + _comment: Metronome Bell + start_freq: 2200 + start_vol: 860 + steps: + - { waveform: triangle , target_freq: 2000, target_vol: 600, duration: 5 } + - { waveform: triangle , target_freq: 1800, target_vol: 300, duration: 8 } + - { waveform: triangle , target_freq: 1500, target_vol: 0, duration: 12 } + - note: 35 + _comment: Acoustic Bass Drum + start_freq: 180 + start_vol: 950 + steps: + - { waveform: noise_tunable , target_freq: 100, target_vol: 700, duration: 5 } + - { waveform: noise_tunable , target_freq: 60, target_vol: 300, duration: 8 } + - { waveform: noise_tunable , target_freq: 40, target_vol: 0, duration: 12 } + - note: 36 + _comment: Bass Drum 1 + start_freq: 160 + start_vol: 950 + steps: + - { waveform: noise_tunable , target_freq: 90, target_vol: 700, duration: 5 } + - { waveform: noise_tunable , target_freq: 50, target_vol: 300, duration: 8 } + - { waveform: noise_tunable , target_freq: 35, target_vol: 0, duration: 10 } + - note: 37 + _comment: Side Stick + start_freq: 900 + start_vol: 920 + steps: + - { waveform: noise , target_freq: 700, target_vol: 500, duration: 3 } + - { waveform: noise , target_freq: 500, target_vol: 0, duration: 5 } + - note: 38 + _comment: Acoustic Snare + start_freq: 400 + start_vol: 940 + steps: + - { waveform: noise , target_freq: 300, target_vol: 600, duration: 5 } + - { waveform: noise , target_freq: 200, target_vol: 200, duration: 8 } + - { waveform: noise , target_freq: 100, target_vol: 0, duration: 10 } + - note: 39 + _comment: Hand Clap + start_freq: 1200 + start_vol: 920 + steps: + - { waveform: noise , target_freq: 900, target_vol: 500, duration: 4 } + - { waveform: noise , target_freq: 600, target_vol: 200, duration: 6 } + - { waveform: noise , target_freq: 300, target_vol: 0, duration: 8 } + - note: 40 + _comment: Electric Snare + start_freq: 350 + start_vol: 930 + steps: + - { waveform: noise , target_freq: 250, target_vol: 600, duration: 5 } + - { waveform: noise , target_freq: 150, target_vol: 200, duration: 7 } + - { waveform: noise , target_freq: 80, target_vol: 0, duration: 9 } + - note: 41 + _comment: Low Floor Tom + start_freq: 100 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 70, target_vol: 600, duration: 7 } + - { waveform: noise_tunable , target_freq: 50, target_vol: 200, duration: 10 } + - { waveform: noise_tunable , target_freq: 35, target_vol: 0, duration: 15 } + - note: 42 + _comment: Closed Hi-Hat + start_freq: 4000 + start_vol: 860 + steps: + - { waveform: noise , target_freq: 3500, target_vol: 400, duration: 3 } + - { waveform: noise , target_freq: 3000, target_vol: 0, duration: 4 } + - note: 43 + _comment: High Floor Tom + start_freq: 130 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 90, target_vol: 600, duration: 7 } + - { waveform: noise_tunable , target_freq: 65, target_vol: 200, duration: 10 } + - { waveform: noise_tunable , target_freq: 45, target_vol: 0, duration: 14 } + - note: 44 + _comment: Pedal Hi-Hat + start_freq: 3500 + start_vol: 800 + steps: + - { waveform: noise , target_freq: 3000, target_vol: 400, duration: 3 } + - { waveform: noise , target_freq: 2500, target_vol: 100, duration: 5 } + - { waveform: noise , target_freq: 2000, target_vol: 0, duration: 6 } + - note: 45 + _comment: Low Tom + start_freq: 150 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 110, target_vol: 600, duration: 7 } + - { waveform: noise_tunable , target_freq: 75, target_vol: 200, duration: 10 } + - { waveform: noise_tunable , target_freq: 50, target_vol: 0, duration: 14 } + - note: 46 + _comment: Open Hi-Hat + start_freq: 4000 + start_vol: 860 + steps: + - { waveform: noise , target_freq: 3800, target_vol: 700, duration: 8 } + - { waveform: noise , target_freq: 3500, target_vol: 400, duration: 14 } + - { waveform: noise , target_freq: 3000, target_vol: 100, duration: 20 } + - { waveform: noise , target_freq: 2500, target_vol: 0, duration: 22 } + - note: 47 + _comment: Low-Mid Tom + start_freq: 180 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 130, target_vol: 600, duration: 7 } + - { waveform: noise_tunable , target_freq: 90, target_vol: 200, duration: 10 } + - { waveform: noise_tunable , target_freq: 60, target_vol: 0, duration: 13 } + - note: 48 + _comment: Hi-Mid Tom + start_freq: 250 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 180, target_vol: 600, duration: 6 } + - { waveform: noise_tunable , target_freq: 120, target_vol: 200, duration: 9 } + - { waveform: noise_tunable , target_freq: 80, target_vol: 0, duration: 12 } + - note: 49 + _comment: Crash Cymbal 1 + start_freq: 5000 + start_vol: 920 + steps: + - { waveform: noise , target_freq: 4500, target_vol: 700, duration: 10 } + - { waveform: noise , target_freq: 4000, target_vol: 400, duration: 18 } + - { waveform: noise , target_freq: 3000, target_vol: 100, duration: 25 } + - { waveform: noise , target_freq: 2000, target_vol: 0, duration: 30 } + - note: 50 + _comment: High Tom + start_freq: 350 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 250, target_vol: 600, duration: 5 } + - { waveform: noise_tunable , target_freq: 160, target_vol: 200, duration: 8 } + - { waveform: noise_tunable , target_freq: 100, target_vol: 0, duration: 11 } + - note: 51 + _comment: Ride Cymbal 1 + start_freq: 4500 + start_vol: 820 + steps: + - { waveform: noise , target_freq: 4200, target_vol: 600, duration: 8 } + - { waveform: noise , target_freq: 4000, target_vol: 350, duration: 15 } + - { waveform: noise , target_freq: 3500, target_vol: 100, duration: 22 } + - { waveform: noise , target_freq: 3000, target_vol: 0, duration: 28 } + - note: 52 + _comment: Chinese Cymbal + start_freq: 5500 + start_vol: 920 + steps: + - { waveform: cycle_64 , target_freq: 5000, target_vol: 700, duration: 8 } + - { waveform: cycle_64 , target_freq: 4500, target_vol: 400, duration: 15 } + - { waveform: cycle_64 , target_freq: 4000, target_vol: 100, duration: 22 } + - { waveform: cycle_64 , target_freq: 3000, target_vol: 0, duration: 28 } + - note: 53 + _comment: Ride Bell + start_freq: 2800 + start_vol: 870 + steps: + - { waveform: triangle , target_freq: 2600, target_vol: 600, duration: 6 } + - { waveform: triangle , target_freq: 2400, target_vol: 300, duration: 12 } + - { waveform: triangle , target_freq: 2200, target_vol: 100, duration: 20 } + - { waveform: triangle , target_freq: 2000, target_vol: 0, duration: 25 } + - note: 54 + _comment: Tambourine + start_freq: 4500 + start_vol: 800 + steps: + - { waveform: noise , target_freq: 4000, target_vol: 500, duration: 5 } + - { waveform: noise , target_freq: 3500, target_vol: 200, duration: 8 } + - { waveform: noise , target_freq: 3000, target_vol: 0, duration: 10 } + - note: 55 + _comment: Splash Cymbal + start_freq: 5500 + start_vol: 890 + steps: + - { waveform: noise , target_freq: 5000, target_vol: 600, duration: 7 } + - { waveform: noise , target_freq: 4500, target_vol: 300, duration: 12 } + - { waveform: noise , target_freq: 4000, target_vol: 0, duration: 16 } + - note: 56 + _comment: Cowbell + start_freq: 600 + start_vol: 910 + steps: + - { waveform: square_50 , target_freq: 580, target_vol: 700, duration: 6 } + - { waveform: square_50 , target_freq: 550, target_vol: 400, duration: 12 } + - { waveform: square_50 , target_freq: 520, target_vol: 100, duration: 18 } + - { waveform: square_50 , target_freq: 500, target_vol: 0, duration: 22 } + - note: 57 + _comment: Crash Cymbal 2 + start_freq: 4800 + start_vol: 920 + steps: + - { waveform: noise , target_freq: 4400, target_vol: 700, duration: 10 } + - { waveform: noise , target_freq: 3900, target_vol: 400, duration: 18 } + - { waveform: noise , target_freq: 3000, target_vol: 100, duration: 25 } + - { waveform: noise , target_freq: 2200, target_vol: 0, duration: 30 } + - note: 58 + _comment: Vibraslap + start_freq: 800 + start_vol: 820 + steps: + - { waveform: cycle_32 , target_freq: 700, target_vol: 600, duration: 8 } + - { waveform: cycle_32 , target_freq: 600, target_vol: 400, duration: 12 } + - { waveform: cycle_32 , target_freq: 500, target_vol: 200, duration: 16 } + - { waveform: cycle_32 , target_freq: 400, target_vol: 0, duration: 18 } + - note: 59 + _comment: Ride Cymbal 2 + start_freq: 4200 + start_vol: 810 + steps: + - { waveform: noise , target_freq: 4000, target_vol: 600, duration: 8 } + - { waveform: noise , target_freq: 3700, target_vol: 350, duration: 15 } + - { waveform: noise , target_freq: 3200, target_vol: 100, duration: 22 } + - { waveform: noise , target_freq: 2800, target_vol: 0, duration: 27 } + - note: 60 + _comment: Hi Bongo + start_freq: 450 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 350, target_vol: 600, duration: 5 } + - { waveform: noise_tunable , target_freq: 250, target_vol: 200, duration: 8 } + - { waveform: noise_tunable , target_freq: 150, target_vol: 0, duration: 11 } + - note: 61 + _comment: Low Bongo + start_freq: 280 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 210, target_vol: 600, duration: 6 } + - { waveform: noise_tunable , target_freq: 150, target_vol: 200, duration: 9 } + - { waveform: noise_tunable , target_freq: 100, target_vol: 0, duration: 12 } + - note: 62 + _comment: Mute Hi Conga + start_freq: 380 + start_vol: 900 + steps: + - { waveform: noise_tunable , target_freq: 300, target_vol: 500, duration: 4 } + - { waveform: noise_tunable , target_freq: 220, target_vol: 100, duration: 6 } + - { waveform: noise_tunable , target_freq: 150, target_vol: 0, duration: 7 } + - note: 63 + _comment: Open Hi Conga + start_freq: 360 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 300, target_vol: 700, duration: 6 } + - { waveform: noise_tunable , target_freq: 240, target_vol: 300, duration: 10 } + - { waveform: noise_tunable , target_freq: 180, target_vol: 0, duration: 14 } + - note: 64 + _comment: Low Conga + start_freq: 220 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 170, target_vol: 700, duration: 7 } + - { waveform: noise_tunable , target_freq: 120, target_vol: 300, duration: 11 } + - { waveform: noise_tunable , target_freq: 80, target_vol: 0, duration: 15 } + - note: 65 + _comment: High Timbale + start_freq: 550 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 420, target_vol: 600, duration: 5 } + - { waveform: noise_tunable , target_freq: 300, target_vol: 200, duration: 8 } + - { waveform: noise_tunable , target_freq: 200, target_vol: 0, duration: 11 } + - note: 66 + _comment: Low Timbale + start_freq: 380 + start_vol: 920 + steps: + - { waveform: noise_tunable , target_freq: 280, target_vol: 600, duration: 6 } + - { waveform: noise_tunable , target_freq: 200, target_vol: 200, duration: 9 } + - { waveform: noise_tunable , target_freq: 130, target_vol: 0, duration: 12 } + - note: 67 + _comment: High Agogo + start_freq: 800 + start_vol: 880 + steps: + - { waveform: square_50 , target_freq: 750, target_vol: 600, duration: 5 } + - { waveform: square_50 , target_freq: 700, target_vol: 250, duration: 10 } + - { waveform: square_50 , target_freq: 650, target_vol: 0, duration: 14 } + - note: 68 + _comment: Low Agogo + start_freq: 600 + start_vol: 880 + steps: + - { waveform: square_50 , target_freq: 560, target_vol: 600, duration: 6 } + - { waveform: square_50 , target_freq: 520, target_vol: 250, duration: 11 } + - { waveform: square_50 , target_freq: 480, target_vol: 0, duration: 15 } + - note: 69 + _comment: Cabasa + start_freq: 4000 + start_vol: 760 + steps: + - { waveform: noise , target_freq: 3500, target_vol: 400, duration: 5 } + - { waveform: noise , target_freq: 3000, target_vol: 150, duration: 8 } + - { waveform: noise , target_freq: 2500, target_vol: 0, duration: 10 } + - note: 70 + _comment: Maracas + start_freq: 5000 + start_vol: 760 + steps: + - { waveform: noise , target_freq: 4500, target_vol: 400, duration: 4 } + - { waveform: noise , target_freq: 4000, target_vol: 0, duration: 6 } + - note: 71 + _comment: Short Whistle + start_freq: 2000 + start_vol: 820 + steps: + - { waveform: sine , target_freq: 2050, target_vol: 500, duration: 6 } + - { waveform: sine , target_freq: 1900, target_vol: 0, duration: 8 } + - note: 72 + _comment: Long Whistle + start_freq: 1800 + start_vol: 830 + steps: + - { waveform: sine , target_freq: 1850, target_vol: 800, duration: 20 } + - { waveform: sine , target_freq: 1800, target_vol: 400, duration: 20 } + - { waveform: sine , target_freq: 1750, target_vol: 0, duration: 15 } + - note: 73 + _comment: Short Guiro + start_freq: 1200 + start_vol: 750 + steps: + - { waveform: cycle_32 , target_freq: 1000, target_vol: 500, duration: 5 } + - { waveform: cycle_32 , target_freq: 800, target_vol: 200, duration: 7 } + - { waveform: cycle_32 , target_freq: 600, target_vol: 0, duration: 8 } + - note: 74 + _comment: Long Guiro + start_freq: 1500 + start_vol: 750 + steps: + - { waveform: cycle_32 , target_freq: 1200, target_vol: 600, duration: 10 } + - { waveform: cycle_32 , target_freq: 900, target_vol: 350, duration: 15 } + - { waveform: cycle_32 , target_freq: 600, target_vol: 100, duration: 18 } + - { waveform: cycle_32 , target_freq: 400, target_vol: 0, duration: 12 } + - note: 75 + _comment: Claves + start_freq: 1200 + start_vol: 960 + steps: + - { waveform: square_50 , target_freq: 1100, target_vol: 400, duration: 3 } + - { waveform: square_50 , target_freq: 900, target_vol: 0, duration: 4 } + - note: 76 + _comment: Hi Wood Block + start_freq: 900 + start_vol: 940 + steps: + - { waveform: square_50 , target_freq: 800, target_vol: 500, duration: 4 } + - { waveform: square_50 , target_freq: 650, target_vol: 100, duration: 6 } + - { waveform: square_50 , target_freq: 500, target_vol: 0, duration: 7 } + - note: 77 + _comment: Low Wood Block + start_freq: 650 + start_vol: 940 + steps: + - { waveform: square_50 , target_freq: 580, target_vol: 500, duration: 5 } + - { waveform: square_50 , target_freq: 470, target_vol: 100, duration: 7 } + - { waveform: square_50 , target_freq: 380, target_vol: 0, duration: 8 } + - note: 78 + _comment: Mute Cuica + start_freq: 500 + start_vol: 860 + steps: + - { waveform: noise_tunable , target_freq: 550, target_vol: 500, duration: 4 } + - { waveform: noise_tunable , target_freq: 500, target_vol: 0, duration: 5 } + - note: 79 + _comment: Open Cuica + start_freq: 400 + start_vol: 870 + steps: + - { waveform: noise_tunable , target_freq: 500, target_vol: 700, duration: 6 } + - { waveform: noise_tunable , target_freq: 650, target_vol: 500, duration: 8 } + - { waveform: noise_tunable , target_freq: 700, target_vol: 200, duration: 10 } + - { waveform: noise_tunable , target_freq: 600, target_vol: 0, duration: 8 } + - note: 80 + _comment: Mute Triangle + start_freq: 3500 + start_vol: 840 + steps: + - { waveform: triangle , target_freq: 3400, target_vol: 500, duration: 4 } + - { waveform: triangle , target_freq: 3200, target_vol: 0, duration: 5 } + - note: 81 + _comment: Open Triangle + start_freq: 3500 + start_vol: 840 + steps: + - { waveform: triangle , target_freq: 3450, target_vol: 800, duration: 15 } + - { waveform: triangle , target_freq: 3400, target_vol: 600, duration: 25 } + - { waveform: triangle , target_freq: 3300, target_vol: 300, duration: 30 } + - { waveform: triangle , target_freq: 3200, target_vol: 0, duration: 30 } + - note: 82 + _comment: Shaker + start_freq: 5000 + start_vol: 760 + steps: + - { waveform: noise , target_freq: 4500, target_vol: 500, duration: 6 } + - { waveform: noise , target_freq: 4000, target_vol: 200, duration: 9 } + - { waveform: noise , target_freq: 3500, target_vol: 0, duration: 10 } + - note: 83 + _comment: Jingle Bell + start_freq: 2500 + start_vol: 820 + steps: + - { waveform: triangle , target_freq: 2400, target_vol: 600, duration: 8 } + - { waveform: triangle , target_freq: 2300, target_vol: 300, duration: 15 } + - { waveform: triangle , target_freq: 2200, target_vol: 100, duration: 20 } + - { waveform: triangle , target_freq: 2100, target_vol: 0, duration: 20 } + - note: 84 + _comment: Bell Tree + start_freq: 1500 + start_vol: 800 + steps: + - { waveform: triangle , target_freq: 2000, target_vol: 700, duration: 10 } + - { waveform: triangle , target_freq: 2500, target_vol: 500, duration: 15 } + - { waveform: triangle , target_freq: 3000, target_vol: 200, duration: 18 } + - { waveform: triangle , target_freq: 3500, target_vol: 0, duration: 20 } + - note: 85 + _comment: Castanets + start_freq: 1400 + start_vol: 950 + steps: + - { waveform: square_50 , target_freq: 1200, target_vol: 400, duration: 3 } + - { waveform: square_50 , target_freq: 900, target_vol: 0, duration: 4 } + - note: 86 + _comment: Mute Surdo + start_freq: 120 + start_vol: 940 + steps: + - { waveform: noise_tunable , target_freq: 90, target_vol: 600, duration: 6 } + - { waveform: noise_tunable , target_freq: 65, target_vol: 200, duration: 8 } + - { waveform: noise_tunable , target_freq: 50, target_vol: 0, duration: 8 } + - note: 87 + _comment: Open Surdo + start_freq: 100 + start_vol: 950 + steps: + - { waveform: noise_tunable , target_freq: 80, target_vol: 700, duration: 8 } + - { waveform: noise_tunable , target_freq: 60, target_vol: 350, duration: 14 } + - { waveform: noise_tunable , target_freq: 45, target_vol: 100, duration: 18 } + - { waveform: noise_tunable , target_freq: 35, target_vol: 0, duration: 20 } diff --git a/src/converter/instruments.py b/src/converter/instruments.py index f002637..1fabb54 100644 --- a/src/converter/instruments.py +++ b/src/converter/instruments.py @@ -82,8 +82,8 @@ def load_instrument_params(yaml_text: str) -> InstrumentParameterMapping: logger.debug(f"Creating mappings for {len(data["melodic_instruments"])} melodic " f"instruments") for instr in data["melodic_instruments"]: - # TODO: Make people define instrument_params.yaml from 0-127 - mapping.melodic_instruments[instr["instrument"] - 1] = Instrument( + # TODO: If pitch envelope or LFOs aren't defined in the YAML don't error out + mapping.melodic_instruments[instr["instrument"]] = Instrument( waveform=waveform_from_str(instr["waveform"]), # Each note can range from 0-63, so we'll have two tracks, each with the diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index 736963d..bbfe92e 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -1,12 +1,12 @@ import logging -from copy import deepcopy +from collections import defaultdict from dataclasses import dataclass from enum import IntEnum from typing import Dict, List, Tuple from mido import Message, MidiFile, tick2second -from arcade.music_types import Envelope, Instrument, Song, Track +from arcade.music_types import Song from converter.instruments import InstrumentParameterMapping from utils.logger import create_logger @@ -358,7 +358,52 @@ def timeline_group_messages(timeline: List[AbsoluteTimeMessageWithInstrument]) - return timeline_with_complete_notes -def find_all_melodic_instruments(timeline: List[AbsoluteCompleteNote]) -> List[int]: +@dataclass +class AbsoluteCompleteNoteWithTick: + start_tick: int + end_tick: int + + note: int + velocity: int + + instrument: int + is_drum: bool + + +def timeline_quantize_to_song_ticks(timeline: List[AbsoluteCompleteNote], + song: Song) -> List[AbsoluteCompleteNoteWithTick]: + """ + Given the song's BPM and TPB, quantize the timeline's start and end times to ticks. + + :param timeline: A list of `AbsoluteCompleteNote` objects. + :param song: The `Song` object to use. + :return: A list of `AbsoluteCompleteNoteWithTick` objects. + """ + tick_time = (60 / song.beats_per_minute) / song.ticks_per_beat # in secs + logger.debug(f"Quantizing note times to ticks based of song BPM of " + f"{song.beats_per_minute} and TPB of {song.ticks_per_beat} - one tick " + f"is 1/{1 / tick_time} ({tick_time}) seconds long") + + res = [] + + for old_note in timeline: + new_start_tick = round(old_note.start_time / tick_time) + # Ensure all notes last for one tick + new_end_tick = max(round(old_note.end_time / tick_time), new_start_tick + 1) + res.append(AbsoluteCompleteNoteWithTick( + start_tick=new_start_tick, + end_tick=new_end_tick, + note=old_note.note, + velocity=old_note.velocity, + instrument=old_note.instrument, + is_drum=old_note.is_drum + )) + + return res + + +def find_all_melodic_instruments(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[ + int]: """ Search the timeline for all unique melodic instruments. @@ -370,7 +415,7 @@ def find_all_melodic_instruments(timeline: List[AbsoluteCompleteNote]) -> List[i return list(sorted(set([m.instrument for m in timeline if not m.is_drum]))) -def find_all_drum_notes_used(timeline: List[AbsoluteCompleteNote]) -> List[int]: +def find_all_drum_notes_used(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[int]: """ Search the timeline for all unique drum notes. @@ -382,95 +427,152 @@ def find_all_drum_notes_used(timeline: List[AbsoluteCompleteNote]) -> List[int]: return list(sorted(set([m.note for m in timeline if m.is_drum]))) +def timeline_group_by_instrument(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[ + List[AbsoluteCompleteNoteWithTick]]: + """ + Split up the timeline by instrument. + + :param timeline: A list of `AbsoluteCompleteNote` objects. + :return: A list of lists of `AbsoluteCompleteNoteWithTick` objects. (Each list of + notes within the list have the same instrument) + """ + logger.debug("Splitting up the timeline by instruments") + + used_melodics = find_all_melodic_instruments(timeline) + used_drums = find_all_drum_notes_used(timeline) + logger.debug(f"Song used {len(used_melodics)} melodic instruments and " + f"{len(used_drums)} unique drum notes") + + tracks = [] + + for melodic in used_melodics: + tracks.append([note for note in timeline if + note.instrument == melodic and not note.is_drum]) + + if len(used_drums) > 0: + tracks.append([note for note in timeline if note.is_drum]) + + logger.debug(f"Split up global timeline into {len(tracks)} tracks") + + return tracks + + +def timeline_split_into_two_tracks_if_needed( + timeline: List[List[AbsoluteCompleteNoteWithTick]]) -> List[ + List[AbsoluteCompleteNoteWithTick]]: + """ + Go through the melodic tracks in the timeline and check the highest and lowest note + in each track. If it can't fit into one track (which has a range limit of 64 notes + from an octave offset) then we use two tracks and move notes as necessary. + + :param timeline: A list of lists of `AbsoluteCompleteNote` objects. + :return: A list of lists of `AbsoluteCompleteNoteWithTick` objects. + """ + logger.debug("Checking necessity to split track into two tracks for range") + + new_tracks: List[List[AbsoluteCompleteNoteWithTick]] = [] + + tracks_that_fit = 0 + tracks_that_split = 0 + + for old_track in timeline: + # drum tracks don't use octave offsets, only 61 samples max as well + if old_track[0].is_drum: + new_tracks.append(old_track) + tracks_that_fit += 1 + continue + + all_notes = [n.note for n in old_track] + highest_note = max(all_notes) + lowest_note = min(all_notes) + + def octave_offset_work(octave: int) -> bool: + return ((((octave - 2) * 12) <= lowest_note) and + (highest_note <= ((octave - 2) * 12 + 63))) + + # does ANY octave offset from [2, 7] work? + if any([octave_offset_work(o) for o in range(2, 8)]): + # we don't need to modify, when constructing the MakeCode Arcade Tracks, + # we'll find the correct octave offset again + new_tracks.append(old_track) + tracks_that_fit += 1 + else: + # split into two tracks, using octave offsets 2 and 7 guarantee covering the + # full MIDI range + low_track = [n for n in old_track if n.note < 64] + high_track = [n for n in old_track if n.note >= 64] + new_tracks.append(low_track) + new_tracks.append(high_track) + tracks_that_split += 1 + + logger.debug(f"{tracks_that_fit} tracks fit within one track's range, " + f"{tracks_that_split} tracks had to split, total of {len(new_tracks)} " + f"tracks in timeline") + + return new_tracks + + @dataclass -class MIDIInstrumentMappingToTrackIDs: - # Map from MIDI instrument number to track IDs - # First in the tuple is the low track (MIDI notes 0-63, octave=2), - # second is the high track (MIDI notes 64-127, octave=7) - melodic_tracks: Dict[int, Tuple[int, int]] - # Track index for the drum track - drum_track: int - # Map from MIDI drum notes to DrumInstrument indicies in the track's drums - drum_notes: Dict[int, int] - - -def assemble_song(melodics_used: List[int], drums_used: List[int], - mapping: InstrumentParameterMapping) -> Tuple[ - Song, MIDIInstrumentMappingToTrackIDs]: +class AbsoluteCompleteChordWithTick: + start_tick: int + end_tick: int + + notes: List[int] + velocity: int + + instrument: int + is_drum: bool + + +def timeline_group_into_perfect_chords( + timeline: List[List[AbsoluteCompleteNoteWithTick]]) -> List[ + List[AbsoluteCompleteChordWithTick]]: """ - Given a list of melodic instruments and whether the drum was used or not, assemble - an empty MakeCode Arcade song with the appropriate tracks, using the instrument - parameter mapping. - - :param melodics_used: A list of general MIDI melodic instrument indicies (ints) that - are used in the song. - :param drums_used: A list of general MIDI drum notes (ints) that are used in the - song. - :param mapping: The `InstrumentParameterMapping` object, loaded from - `load_instrument_params`. - :return: MakeCode Arcade `Song` object with the correct tracks loaded. + Go through the tracks in the timeline and group up notes that share the same start + and end tick and velocity and instruments to create "perfect" chords. + + :param timeline: A list of lists of `AbsoluteCompleteNoteWithTick` objects. + :return: A list of lists of `AbsoluteCompleteChordWithTick` objects. """ - logger.debug(f"Assembling song with {len(melodics_used)} melodic instruments and " - f"{len(drums_used)} drum notes") + logger.debug("Grouping notes in timeline into perfect chords") + + new_tracks: List[List[AbsoluteCompleteChordWithTick]] = [] + + old_note_count = sum(len(track) for track in timeline) + + for old_track in timeline: + # use dictionaries to quickly find existing chords + # key is a tuple of (start_tick, end_tick, velocity, instrument, is_drum) + # values are notes in the chord + chords: Dict[Tuple[int, int, int, int, bool], List[int]] = defaultdict(list) + + for note in old_track: + key = (note.start_tick, note.end_tick, note.velocity, note.instrument, + note.is_drum) + # if a note has the same start and end tick and velocity and instrument + # they can be played as a chord + chords[key].append(note.note) + + new_track: List[AbsoluteCompleteChordWithTick] = [] + for (start_tick, end_tick, velocity, instrument, + is_drum), notes in chords.items(): + new_track.append(AbsoluteCompleteChordWithTick( + start_tick=start_tick, end_tick=end_tick, + notes=notes, + velocity=velocity, + instrument=instrument, is_drum=is_drum + )) + new_track.sort(key=lambda c: (c.start_tick, c.end_tick)) + new_tracks.append(new_track) - song = Song( # TODO FIGURE OUT GOOD SETTINGS FOR THESE THREE - measures=0, - beats_per_measure=4, - beats_per_minute=120, - ticks_per_beat=8, - tracks=[] - ) - id_map = MIDIInstrumentMappingToTrackIDs(melodic_tracks={}, drum_track=-1, - drum_notes={}) - next_track_id = 0 - - for melodic in melodics_used: - id_map.melodic_tracks[melodic] = (next_track_id, next_track_id + 1) - - low_track = Track( - id=next_track_id, - instrument=deepcopy(mapping.melodic_instruments[melodic]), - notes=[], - name=f"MIDI instrument {melodic} low track", - ) - low_track.instrument.octave = 2 - next_track_id += 1 - - high_track = Track( - id=next_track_id, - instrument=deepcopy(mapping.melodic_instruments[melodic]), - notes=[], - name=f"MIDI instrument {melodic} high track", - ) - high_track.instrument.octave = 7 - next_track_id += 1 - - song.tracks.append(low_track) - song.tracks.append(high_track) - - if len(drums_used) > 0: - id_map.drum_track = next_track_id - song.tracks.append(Track( - id=next_track_id, - drums=[], - instrument=Instrument( - waveform=11, - octave=4, - amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=100, - amplitude=1024) - ), - name="MIDI drum track", - notes=[] - )) + new_chord_count = sum(len(track) for track in new_tracks) - next_drum_index = 0 - for drum in drums_used: - id_map.drum_notes[drum] = next_drum_index - # noinspection PyUnresolvedReferences - song.tracks[-1].drums.append(mapping.drum_instruments[drum]) - next_drum_index += 1 + logger.debug(f"Created " + f"{sum([sum([1 if len(n.notes) > 1 else 0 for n in t]) for t in new_tracks])}" + f" multi-note chords (dropped from {old_note_count} notes to " + f"{new_chord_count} chords)") - return song, id_map + return new_tracks def convert_midi_to_song(midi_song: MidiFile, @@ -492,11 +594,23 @@ def convert_midi_to_song(midi_song: MidiFile, global_timeline: List[AbsoluteCompleteNote] = timeline_group_messages( global_timeline) - melodics_used = find_all_melodic_instruments(global_timeline) - drums_used = find_all_drum_notes_used(global_timeline) - logger.debug(f"Song used {len(melodics_used)} melodic instruments and " - f"{len(drums_used)} unique drum notes") + song = Song( + measures=1, + beats_per_measure=4, + beats_per_minute=120, # beat every 1/2 seconds + ticks_per_beat=24, # each tick is 1/48 seconds long + tracks=[] + ) - song, track_id_map = assemble_song(melodics_used, drums_used, mapping) + global_timeline: List[ + AbsoluteCompleteNoteWithTick] = timeline_quantize_to_song_ticks(global_timeline, + song) + global_timeline: List[ + List[AbsoluteCompleteNoteWithTick]] = timeline_group_by_instrument( + global_timeline) + global_timeline = timeline_split_into_two_tracks_if_needed(global_timeline) + global_timeline: List[ + List[AbsoluteCompleteChordWithTick]] = timeline_group_into_perfect_chords( + global_timeline) return song From 9e648005dc7fbd2fbfb6efb374a2680eb099a663 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sat, 30 May 2026 23:57:42 -0400 Subject: [PATCH 12/22] Resolve overlapping chords into distinct tracks --- src/converter/midi_to_song.py | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index bbfe92e..f5aca6b 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -575,6 +575,53 @@ def timeline_group_into_perfect_chords( return new_tracks +def timeline_resolve_overlapping_chords( + timeline: List[List[AbsoluteCompleteChordWithTick]]) -> List[ + List[AbsoluteCompleteChordWithTick]]: + """ + Go through the melodic tracks in the timeline and check for chords overlapping in a + track. If they are, move them to an extra track. If there are no free tracks, create + one. This minimizes the number of extra tracks required while keeping the desired + polyphony of MIDI. + + :param timeline: A list of lists of `AbsoluteCompleteChordWithTick` objects. + :return: A list of lists of `AbsoluteCompleteChordWithTick` objects. + """ + logger.debug("Resolving overlapping chords") + + new_tracks: List[List[AbsoluteCompleteChordWithTick]] = [] + old_track_count = len(timeline) + + for old_track in timeline: + new_sub_tracks: List[List[AbsoluteCompleteChordWithTick]] = [[]] + + # don't have to worry about instrument matching because chords in old_track + # should all have the same instrument + for chord in old_track: + # try to place into a sub track + for sub_track in new_sub_tracks: + # if the last note in the subtrack has ended (or it's empty) + # TODO: Test if this needs to be < or <= works + # theoretically it should work fine with <= (and this will save tracks) + # but < will guarantee a "rest" + if len(sub_track) == 0 or sub_track[-1].end_tick < chord.start_tick: + sub_track.append(chord) + break + else: + # no free sub tracks, create + new_sub_tracks.append([chord]) + + # dump all generated sub tracks directly into the new timeline + new_tracks.extend(new_sub_tracks) + + new_track_count = len(new_tracks) + logger.debug(f"Created {new_track_count - old_track_count} extra tracks to handle " + f"overlapping chords (from {old_track_count} to {new_track_count} " + f"tracks)") + + return new_tracks + + def convert_midi_to_song(midi_song: MidiFile, mapping: InstrumentParameterMapping) -> Song: """ @@ -612,5 +659,8 @@ def convert_midi_to_song(midi_song: MidiFile, global_timeline: List[ List[AbsoluteCompleteChordWithTick]] = timeline_group_into_perfect_chords( global_timeline) + global_timeline: List[ + List[AbsoluteCompleteChordWithTick]] = timeline_resolve_overlapping_chords( + global_timeline) return song From 472a6c07e472b8e8ce3848d4f05c16698ce5b257 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 31 May 2026 00:38:17 -0400 Subject: [PATCH 13/22] Add timeline checks to validate song structure before conversion --- src/converter/midi_to_song.py | 90 ++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index f5aca6b..225bedf 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -622,6 +622,73 @@ def timeline_resolve_overlapping_chords( return new_tracks +def timeline_checks(timeline: List[List[AbsoluteCompleteChordWithTick]]): + """ + Run some basic checks on the timeline to verify assumptions before mapping to the + MakeCode Arcade dataclasses. + + :param timeline: A list of lists of `AbsoluteCompleteChordWithTick` objects. + :raises ValueError: If any violations are detected. + """ + logger.debug("Running checks on the timeline") + # Track count must be less than 256 + if len(timeline) > 255: + raise ValueError(f"Too many tracks in the timeline! (max of 255, found " + f"{len(timeline)}) Reduce polyphony or unique instruments.") + for track in timeline: + if len(track) == 0: + raise Warning("Empty track in the timeline!") + # Sort by start tick as required, just in case + track.sort(key=lambda c: c.start_tick) + # The start and end ticks must be less than 65536 + highest_tick = max( + [chord.start_tick for chord in track] + [chord.end_tick for chord in track]) + if highest_tick > 65535: + raise ValueError(f"Chord start or end tick is too high! (max of 65535, " + f"found {highest_tick}) Decrease song length or reduce " + f"BPM/TPB at the cost of worse timing.") + # The highest and lowest notes must fit within 64 notes of an integer octave + # offset for melodic instruments + if not track[0].is_drum: + highest_note = max([max(chord.notes) for chord in track]) + lowest_note = min([min(chord.notes) for chord in track]) + + def octave_offset_work(octave: int) -> bool: + return ((((octave - 2) * 12) <= lowest_note) and + (highest_note <= ((octave - 2) * 12 + 63))) + + if not any([octave_offset_work(o) for o in range(2, 8)]): + raise ValueError(f"Track range too big to fit! (please report)") + # Chord must have less than 256 notes + max_chord_polyphony = max([len(chord.notes) for chord in track]) + if max_chord_polyphony > 255: + raise ValueError(f"Chord has too many notes! (max of 255, found " + f"{max_chord_polyphony})") + # Chords must last at least 1 tick + min_chord_duration = min([chord.end_tick - chord.start_tick for chord in track]) + if min_chord_duration < 1: + raise ValueError(f"Chord violated minimum duration time! (min of 1 tick, " + f"found {min_chord_duration} ticks, please report)") + # All chords must have the same instrument if melodic or all chords must be + # marked as drums in a drum track + if track[0].is_drum: + if any([not chord.is_drum for chord in track]): + raise ValueError(f"Found non drum chord in drum track! (please report") + else: + instruments = set([chord.instrument for chord in track]) + if len(instruments) > 1: + raise ValueError(f"Track has more than one instrument! (found " + f"{len(instruments)}, please report)") + # No chords overlap, i.e. start_tick >= end_tick of prev chord + for i in range(1, len(track)): + prev_chord = track[i - 1] + this_chord = track[i] + if prev_chord.end_tick > this_chord.start_tick: + raise ValueError(f"Chord overlapped! (please report)") + + logger.debug("Timeline passes all checks") + + def convert_midi_to_song(midi_song: MidiFile, mapping: InstrumentParameterMapping) -> Song: """ @@ -634,13 +701,6 @@ def convert_midi_to_song(midi_song: MidiFile, """ logger.debug("Converting MIDI file into MakeCode Arcade song") - global_timeline: List[AbsoluteTimeMessage] = timeline_build(midi_song) - global_timeline: List[ - AbsoluteTimeMessageWithInstrument] = timeline_find_instrument_data( - global_timeline) - global_timeline: List[AbsoluteCompleteNote] = timeline_group_messages( - global_timeline) - song = Song( measures=1, beats_per_measure=4, @@ -649,6 +709,14 @@ def convert_midi_to_song(midi_song: MidiFile, tracks=[] ) + logger.debug("Resolving timeline") + + global_timeline: List[AbsoluteTimeMessage] = timeline_build(midi_song) + global_timeline: List[ + AbsoluteTimeMessageWithInstrument] = timeline_find_instrument_data( + global_timeline) + global_timeline: List[AbsoluteCompleteNote] = timeline_group_messages( + global_timeline) global_timeline: List[ AbsoluteCompleteNoteWithTick] = timeline_quantize_to_song_ticks(global_timeline, song) @@ -663,4 +731,12 @@ def convert_midi_to_song(midi_song: MidiFile, List[AbsoluteCompleteChordWithTick]] = timeline_resolve_overlapping_chords( global_timeline) + # Raises exceptions on check failures + timeline_checks(global_timeline) + + # With all this pitch checks and timing manipulations done to fit MakeCode Arcade's + # song's constraints, we should be able to basically map 1-1 to the MakeCode Arcade + # dataclasses + logger.debug("Timeline resolved, mapping to MakeCode Arcade song") + return song From a2781be4277a6f75e71744ffd03f8ce0de5d1b1e Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 31 May 2026 17:42:17 -0400 Subject: [PATCH 14/22] SOMETHING WORKS --- src/converter/midi_to_song.py | 95 ++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index 225bedf..0f0eeba 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -1,12 +1,15 @@ import logging from collections import defaultdict +from copy import deepcopy from dataclasses import dataclass from enum import IntEnum +from math import ceil from typing import Dict, List, Tuple from mido import Message, MidiFile, tick2second -from arcade.music_types import Song +from arcade.music_types import EnharmonicSpelling, Envelope, Instrument, Note, \ + NoteEvent, Song, Track from converter.instruments import InstrumentParameterMapping from utils.logger import create_logger @@ -427,6 +430,20 @@ def find_all_drum_notes_used(timeline: List[AbsoluteCompleteNoteWithTick]) -> Li return list(sorted(set([m.note for m in timeline if m.is_drum]))) +def find_all_drum_chords_used(timeline: List[AbsoluteCompleteChordWithTick]) -> List[ + int]: + """ + Search the timeline for all unique drum notes, for a list of chords. + + :param timeline: A list of `AbsoluteCompleteChordWithTick` objects. + :return: A list of ints, representing what general MIDI drum notes are in the song. + """ + logger.debug(f"Finding all drum chords in the timeline") + + return list( + sorted({note for chord in timeline if chord.is_drum for note in chord.notes})) + + def timeline_group_by_instrument(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[ List[AbsoluteCompleteNoteWithTick]]: """ @@ -739,4 +756,80 @@ def convert_midi_to_song(midi_song: MidiFile, # dataclasses logger.debug("Timeline resolved, mapping to MakeCode Arcade song") + next_id = 0 + highest_tick = 0 + + for old_track in global_timeline: + this_track_is_drum = old_track[0].is_drum + highest_tick = max([highest_tick] + [c.end_tick for c in old_track]) + + # for drums + midi_drum_to_drum_idx: Dict[int, int] = {} + if this_track_is_drum: + # shouldn't matter, copied from get_empty_song to satisfy types and song + # packing + instrument = Instrument( + waveform=11, + octave=4, + amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=100, + amplitude=1024) + ) + # actually load the drums in + # and keep what midi note to what sample index they should go to + drums = [] + used_drum_notes = find_all_drum_chords_used(old_track) + for i, drum_note in enumerate(used_drum_notes): + drums.append(mapping.drum_instruments[drum_note]) + midi_drum_to_drum_idx[drum_note] = i + else: + instrument = deepcopy(mapping.melodic_instruments[old_track[0].instrument]) + # determine the optimal octave offset + highest_note = max([max(chord.notes) for chord in old_track]) + lowest_note = min([min(chord.notes) for chord in old_track]) + + def octave_offset_work(octave: int) -> bool: + return ((((octave - 2) * 12) <= lowest_note) and + (highest_note <= ((octave - 2) * 12 + 63))) + + for potential_offset in range(2, 8): # find the first offset that works + if octave_offset_work(potential_offset): + instrument.octave = potential_offset + break + else: + raise ValueError(f"Track range too big to fit! (please report)") + # none for melodic instrument + drums = None + new_track = Track( + id=next_id, + instrument=instrument, + drums=drums, + notes=[], + ) + for chord in old_track: + if this_track_is_drum: + notes = [midi_drum_to_drum_idx[note] for note in + chord.notes] + else: + offset = (instrument.octave - 2) * 12 + notes = [note - offset for note in chord.notes] + new_track.notes.append(NoteEvent( + notes=[Note(note=n, enharmonic_spelling=EnharmonicSpelling.NORMAL) for n + in notes], + start_tick=chord.start_tick, + end_tick=chord.end_tick, + velocity=chord.velocity + )) + + song.tracks.append(new_track) + next_id += 1 + + # fix the ending measure count + ticks_per_measure = song.beats_per_measure * song.ticks_per_beat + song.measures = ceil(highest_tick / ticks_per_measure) + + time_for_tick = (60 / song.beats_per_minute) / song.ticks_per_beat + logger.debug(f"Finished mapping to MakeCode Arcade song with {len(song.tracks)} " + f"tracks, length of {highest_tick} ticks which is " + f"{highest_tick * time_for_tick} seconds") + return song From 0a865946b194d240f8b7c3e332e06a78cbeb3fab Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 31 May 2026 18:30:25 -0400 Subject: [PATCH 15/22] Don't do offset in convert_midi_to_song --- src/converter/midi_to_song.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index 0f0eeba..7e0ebf8 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -810,8 +810,7 @@ def octave_offset_work(octave: int) -> bool: notes = [midi_drum_to_drum_idx[note] for note in chord.notes] else: - offset = (instrument.octave - 2) * 12 - notes = [note - offset for note in chord.notes] + notes = chord.notes new_track.notes.append(NoteEvent( notes=[Note(note=n, enharmonic_spelling=EnharmonicSpelling.NORMAL) for n in notes], From da321491bedd3a08071538cd61aae85beeb7c6f5 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 31 May 2026 19:31:40 -0400 Subject: [PATCH 16/22] Adjust octave offset range and correct note mapping for MakeCode Arcade --- example/instrument_params.yaml | 1277 -------------------------------- src/converter/midi_to_song.py | 27 +- 2 files changed, 19 insertions(+), 1285 deletions(-) delete mode 100644 example/instrument_params.yaml diff --git a/example/instrument_params.yaml b/example/instrument_params.yaml deleted file mode 100644 index 1cf974c..0000000 --- a/example/instrument_params.yaml +++ /dev/null @@ -1,1277 +0,0 @@ -_comment: " -This file was generated by Gemini for testing purposes. - -Units: - Attack, decay, release: milliseconds - Sustain and amplitudes: range 0-1024 - LFO frequencies: hz - -Waveforms map to as expected by MakeCode Arcade: - triangle = 1 - sawtooth = 2 - sine = 3 - noise_tunable = 4 - noise = 5 - square_10 = 11 - square_20 = 12 - square_30 = 13 - square_40 = 14 - square_50 (or square) = 15 - cycle_16 = 16 - cycle_32 = 17 - cycle_64 (or noise_tunable_2) = 18 - -https://arcade.makecode.com/developer/sound -> The noise is an actual white noise. It ignores the requested frequency or -> note. -> -> The tunable noise tone contains a pseudorandom sample. At high frequency it -> sounds similar to white noise, at low frequency it produces irregular -> crackling noises. -> -> The cycle 16, cycle 32, and cycle 64 waveforms are repeating pseudorandom -> patterns with a cycle length of 16/32/64 samples respectively. They sound -> like a noise-distorted square wave, with short cycles being more regular and -> long cycles being noisier. -" -melodic_instruments: - - instrument: 0 - _comment: Acoustic Grand Piano - waveform: triangle - amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 1 - _comment: Bright Acoustic Piano - waveform: triangle - amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 2 - _comment: Electric Grand Piano - waveform: triangle - amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 3 - _comment: Honky-tonk Piano - waveform: sawtooth - amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 4 - _comment: Electric Piano 1 - waveform: triangle - amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 5 - _comment: Electric Piano 2 - waveform: triangle - amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 6 - _comment: Harpsichord - waveform: square_20 - amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 7 - _comment: Clavi - waveform: sawtooth - amp_envelope: { attack: 5, decay: 400, sustain: 100, release: 200, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 8 - _comment: Celesta - waveform: sine - amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 9 - _comment: Glockenspiel - waveform: sine - amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 10 - _comment: Music Box - waveform: sine - amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 11 - _comment: Vibraphone - waveform: sine - amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 12 - _comment: Marimba - waveform: triangle - amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 13 - _comment: Xylophone - waveform: triangle - amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 14 - _comment: Tubular Bells - waveform: triangle - amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 15 - _comment: Dulcimer - waveform: triangle - amp_envelope: { attack: 2, decay: 200, sustain: 0, release: 100, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 16 - _comment: Drawbar Organ - waveform: square_50 - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 6, amplitude: 15 } - - instrument: 17 - _comment: Percussive Organ - waveform: square_50 - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 18 - _comment: Rock Organ - waveform: square_50 - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 6, amplitude: 15 } - - instrument: 19 - _comment: Church Organ - waveform: square_50 - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 20 - _comment: Reed Organ - waveform: sawtooth - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 21 - _comment: Accordion - waveform: sawtooth - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 22 - _comment: Harmonica - waveform: sawtooth - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 23 - _comment: Tango Accordion - waveform: sawtooth - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 50, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 24 - _comment: Acoustic Guitar (nylon) - waveform: triangle - amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 25 - _comment: Acoustic Guitar (steel) - waveform: triangle - amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 26 - _comment: Electric Guitar (jazz) - waveform: triangle - amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 27 - _comment: Electric Guitar (clean) - waveform: triangle - amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 28 - _comment: Electric Guitar (muted) - waveform: square_30 - amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 29 - _comment: Overdriven Guitar - waveform: sawtooth - amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 30 - _comment: Distortion Guitar - waveform: sawtooth - amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 31 - _comment: Guitar harmonics - waveform: triangle - amp_envelope: { attack: 8, decay: 350, sustain: 150, release: 250, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 32 - _comment: Acoustic Bass - waveform: triangle - amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 33 - _comment: Electric Bass (finger) - waveform: triangle - amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 34 - _comment: Electric Bass (pick) - waveform: triangle - amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 35 - _comment: Fretless Bass - waveform: triangle - amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 36 - _comment: Slap Bass 1 - waveform: square_10 - amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 37 - _comment: Slap Bass 2 - waveform: square_10 - amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 38 - _comment: Synth Bass 1 - waveform: square_10 - amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 39 - _comment: Synth Bass 2 - waveform: square_10 - amp_envelope: { attack: 12, decay: 250, sustain: 300, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 40 - _comment: Violin - waveform: sawtooth - amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 41 - _comment: Viola - waveform: sawtooth - amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 42 - _comment: Cello - waveform: sawtooth - amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 43 - _comment: Contrabass - waveform: sawtooth - amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 44 - _comment: Tremolo Strings - waveform: sawtooth - amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 45 - _comment: Pizzicato Strings - waveform: triangle - amp_envelope: { attack: 2, decay: 100, sustain: 0, release: 50, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 46 - _comment: Orchestral Harp - waveform: sawtooth - amp_envelope: { attack: 120, decay: 200, sustain: 700, release: 250, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 20 } - - instrument: 47 - _comment: Timpani - waveform: triangle - amp_envelope: { attack: 5, decay: 400, sustain: 0, release: 300, amplitude: 1000 } - pitch_envelope: { attack: 0, decay: 150, sustain: 0, release: 0, amplitude: -50 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 48 - _comment: String Ensemble 1 - waveform: sawtooth - amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 49 - _comment: String Ensemble 2 - waveform: sawtooth - amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 50 - _comment: SynthStrings 1 - waveform: sawtooth - amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 51 - _comment: SynthStrings 2 - waveform: sawtooth - amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 52 - _comment: Choir Aahs - waveform: sawtooth - amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 53 - _comment: Voice Oohs - waveform: sawtooth - amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 54 - _comment: Synth Voice - waveform: sawtooth - amp_envelope: { attack: 150, decay: 200, sustain: 750, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 4, amplitude: 15 } - - instrument: 55 - _comment: Orchestra Hit - waveform: sawtooth - amp_envelope: { attack: 5, decay: 150, sustain: 100, release: 100, amplitude: 1024 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 56 - _comment: Trumpet - waveform: sawtooth - amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 57 - _comment: Trombone - waveform: sawtooth - amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 58 - _comment: Tuba - waveform: sawtooth - amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 59 - _comment: Muted Trumpet - waveform: square_40 - amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 60 - _comment: French Horn - waveform: sawtooth - amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 61 - _comment: Brass Section - waveform: sawtooth - amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 62 - _comment: SynthBrass 1 - waveform: square_40 - amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 63 - _comment: SynthBrass 2 - waveform: square_40 - amp_envelope: { attack: 60, decay: 100, sustain: 700, release: 180, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 64 - _comment: Soprano Sax - waveform: square_50 - amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 65 - _comment: Alto Sax - waveform: square_50 - amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 66 - _comment: Tenor Sax - waveform: square_50 - amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 67 - _comment: Baritone Sax - waveform: square_50 - amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 68 - _comment: Oboe - waveform: square_50 - amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 69 - _comment: English Horn - waveform: square_50 - amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 70 - _comment: Bassoon - waveform: square_50 - amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 71 - _comment: Clarinet - waveform: square_50 - amp_envelope: { attack: 40, decay: 80, sustain: 750, release: 120, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 12 } - - instrument: 72 - _comment: Piccolo - waveform: sine - amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 4, amplitude: 30 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 73 - _comment: Flute - waveform: sine - amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 4, amplitude: 30 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 74 - _comment: Recorder - waveform: sine - amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 4, amplitude: 30 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 75 - _comment: Pan Flute - waveform: triangle - amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 4, amplitude: 30 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 76 - _comment: Blown Bottle - waveform: triangle - amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 4, amplitude: 30 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 77 - _comment: Shakuhachi - waveform: triangle - amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 4, amplitude: 30 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 78 - _comment: Whistle - waveform: triangle - amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 4, amplitude: 30 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 79 - _comment: Ocarina - waveform: triangle - amp_envelope: { attack: 50, decay: 100, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 4, amplitude: 30 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 80 - _comment: Lead 1 (square) - waveform: square_50 - amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 81 - _comment: Lead 2 (sawtooth) - waveform: sawtooth - amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 82 - _comment: Lead 3 (calliope) - waveform: square_50 - amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 83 - _comment: Lead 4 (chiff) - waveform: sawtooth - amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 84 - _comment: Lead 5 (charang) - waveform: sawtooth - amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 85 - _comment: Lead 6 (voice) - waveform: sawtooth - amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 86 - _comment: Lead 7 (fifths) - waveform: sawtooth - amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 87 - _comment: Lead 8 (bass + lead) - waveform: sawtooth - amp_envelope: { attack: 10, decay: 100, sustain: 800, release: 150, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 88 - _comment: Pad 1 (new age) - waveform: triangle - amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 89 - _comment: Pad 2 (warm) - waveform: sine - amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 90 - _comment: Pad 3 (polysynth) - waveform: triangle - amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 91 - _comment: Pad 4 (choir) - waveform: sine - amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 92 - _comment: Pad 5 (bowed) - waveform: triangle - amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 93 - _comment: Pad 6 (metallic) - waveform: triangle - amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 94 - _comment: Pad 7 (halo) - waveform: triangle - amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 95 - _comment: Pad 8 (sweep) - waveform: triangle - amp_envelope: { attack: 250, decay: 300, sustain: 800, release: 400, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 96 - _comment: FX 1 (rain) - waveform: cycle_32 - amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 97 - _comment: FX 2 (soundtrack) - waveform: sawtooth - amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 98 - _comment: FX 3 (crystal) - waveform: sawtooth - amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 99 - _comment: FX 4 (atmosphere) - waveform: sawtooth - amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 100 - _comment: FX 5 (brightness) - waveform: sawtooth - amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 101 - _comment: FX 6 (goblins) - waveform: cycle_32 - amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 102 - _comment: FX 7 (echoes) - waveform: sawtooth - amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 103 - _comment: FX 8 (sci-fi) - waveform: sawtooth - amp_envelope: { attack: 80, decay: 200, sustain: 600, release: 300, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 104 - _comment: Sitar - waveform: sawtooth - amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 105 - _comment: Banjo - waveform: triangle - amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 106 - _comment: Shamisen - waveform: triangle - amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 107 - _comment: Koto - waveform: triangle - amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 108 - _comment: Kalimba - waveform: triangle - amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 109 - _comment: Bagpipe - waveform: sawtooth - amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 110 - _comment: Fiddle - waveform: sawtooth - amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 111 - _comment: Shanai - waveform: triangle - amp_envelope: { attack: 15, decay: 250, sustain: 100, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 112 - _comment: Tinkle Bell - waveform: sine - amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 113 - _comment: Agogo - waveform: triangle - amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 114 - _comment: Steel Drums - waveform: sine - amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 115 - _comment: Woodblock - waveform: triangle - amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 116 - _comment: Taiko Drum - waveform: cycle_16 - amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 117 - _comment: Melodic Tom - waveform: cycle_16 - amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 118 - _comment: Synth Drum - waveform: triangle - amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 119 - _comment: Reverse Cymbal - waveform: triangle - amp_envelope: { attack: 5, decay: 150, sustain: 0, release: 100, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 120 - _comment: Guitar Fret Noise - waveform: noise_tunable - amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 121 - _comment: Breath Noise - waveform: noise_tunable - amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 122 - _comment: Seashore - waveform: noise - amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 123 - _comment: Bird Tweet - waveform: noise_tunable - amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 124 - _comment: Telephone Ring - waveform: noise_tunable - amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 125 - _comment: Helicopter - waveform: noise - amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 126 - _comment: Applause - waveform: noise - amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 127 - _comment: Gunshot - waveform: noise - amp_envelope: { attack: 20, decay: 300, sustain: 200, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } -drum_instruments: - - note: 35 - _comment: Acoustic Bass Drum - start_freq: 150 - start_vol: 1024 - steps: - - { waveform: sine, target_freq: 80, target_vol: 900, duration: 30 } - - { waveform: sine, target_freq: 50, target_vol: 400, duration: 40 } - - { waveform: sine, target_freq: 40, target_vol: 0, duration: 60 } - - note: 36 - _comment: Bass Drum 1 - start_freq: 150 - start_vol: 1024 - steps: - - { waveform: sine, target_freq: 80, target_vol: 900, duration: 30 } - - { waveform: sine, target_freq: 50, target_vol: 400, duration: 40 } - - { waveform: sine, target_freq: 40, target_vol: 0, duration: 60 } - - note: 37 - _comment: Side Stick - start_freq: 600 - start_vol: 900 - steps: - - { waveform: triangle, target_freq: 600, target_vol: 400, duration: 15 } - - { waveform: triangle, target_freq: 500, target_vol: 0, duration: 20 } - - note: 38 - _comment: Acoustic Snare - start_freq: 300 - start_vol: 1024 - steps: - - { waveform: noise, target_freq: 300, target_vol: 800, duration: 20 } - - { waveform: noise, target_freq: 200, target_vol: 400, duration: 50 } - - { waveform: noise, target_freq: 100, target_vol: 0, duration: 80 } - - note: 39 - _comment: Hand Clap - start_freq: 1000 - start_vol: 1024 - steps: - - { waveform: noise, target_freq: 1000, target_vol: 0, duration: 10 } - - { waveform: noise, target_freq: 1000, target_vol: 800, duration: 15 } - - { waveform: noise, target_freq: 800, target_vol: 0, duration: 15 } - - { waveform: noise, target_freq: 900, target_vol: 700, duration: 20 } - - { waveform: noise, target_freq: 600, target_vol: 0, duration: 60 } - - note: 40 - _comment: Electric Snare - start_freq: 300 - start_vol: 1024 - steps: - - { waveform: noise, target_freq: 300, target_vol: 800, duration: 20 } - - { waveform: noise, target_freq: 200, target_vol: 400, duration: 50 } - - { waveform: noise, target_freq: 100, target_vol: 0, duration: 80 } - - note: 41 - _comment: Low Floor Tom - start_freq: 120 - start_vol: 950 - steps: - - { waveform: sine, target_freq: 84, target_vol: 600, duration: 40 } - - { waveform: sine, target_freq: 60, target_vol: 0, duration: 80 } - - note: 42 - _comment: Closed Hi Hat - start_freq: 8000 - start_vol: 800 - steps: - - { waveform: noise, target_freq: 6000, target_vol: 300, duration: 12 } - - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 28 } - - note: 43 - _comment: High Floor Tom - start_freq: 120 - start_vol: 950 - steps: - - { waveform: sine, target_freq: 84, target_vol: 600, duration: 40 } - - { waveform: sine, target_freq: 60, target_vol: 0, duration: 80 } - - note: 44 - _comment: Pedal Hi-Hat - start_freq: 8000 - start_vol: 800 - steps: - - { waveform: noise, target_freq: 6000, target_vol: 300, duration: 12 } - - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 28 } - - note: 45 - _comment: Low Tom - start_freq: 180 - start_vol: 950 - steps: - - { waveform: sine, target_freq: 126, target_vol: 600, duration: 40 } - - { waveform: sine, target_freq: 90, target_vol: 0, duration: 80 } - - note: 46 - _comment: Open Hi-Hat - start_freq: 8000 - start_vol: 800 - steps: - - { waveform: noise, target_freq: 6000, target_vol: 300, duration: 45 } - - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 105 } - - note: 47 - _comment: Low-Mid Tom - start_freq: 180 - start_vol: 950 - steps: - - { waveform: sine, target_freq: 126, target_vol: 600, duration: 40 } - - { waveform: sine, target_freq: 90, target_vol: 0, duration: 80 } - - note: 48 - _comment: Hi-Mid Tom - start_freq: 250 - start_vol: 950 - steps: - - { waveform: sine, target_freq: 175, target_vol: 600, duration: 40 } - - { waveform: sine, target_freq: 125, target_vol: 0, duration: 80 } - - note: 49 - _comment: Crash Cymbal 1 - start_freq: 5000 - start_vol: 850 - steps: - - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } - - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } - - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } - - note: 50 - _comment: High Tom - start_freq: 250 - start_vol: 950 - steps: - - { waveform: sine, target_freq: 175, target_vol: 600, duration: 40 } - - { waveform: sine, target_freq: 125, target_vol: 0, duration: 80 } - - note: 51 - _comment: Ride Cymbal 1 - start_freq: 5000 - start_vol: 850 - steps: - - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } - - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } - - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } - - note: 52 - _comment: Chinese Cymbal - start_freq: 5000 - start_vol: 850 - steps: - - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } - - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } - - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } - - note: 53 - _comment: Ride Bell - start_freq: 5000 - start_vol: 850 - steps: - - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } - - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } - - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } - - note: 54 - _comment: Tambourine - start_freq: 6000 - start_vol: 750 - steps: - - { waveform: noise, target_freq: 5000, target_vol: 200, duration: 25 } - - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 35 } - - note: 55 - _comment: Splash Cymbal - start_freq: 5000 - start_vol: 850 - steps: - - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } - - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } - - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } - - note: 56 - _comment: Cowbell - start_freq: 540 - start_vol: 900 - steps: - - { waveform: square_40, target_freq: 540, target_vol: 600, duration: 30 } - - { waveform: square_40, target_freq: 540, target_vol: 0, duration: 120 } - - note: 57 - _comment: Crash Cymbal 2 - start_freq: 5000 - start_vol: 850 - steps: - - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } - - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } - - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } - - note: 58 - _comment: Vibraslap - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 59 - _comment: Ride Cymbal 2 - start_freq: 5000 - start_vol: 850 - steps: - - { waveform: cycle_64, target_freq: 4000, target_vol: 600, duration: 80 } - - { waveform: noise, target_freq: 3000, target_vol: 300, duration: 200 } - - { waveform: noise, target_freq: 2000, target_vol: 0, duration: 300 } - - note: 60 - _comment: Hi Bongo - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 61 - _comment: Low Bongo - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 62 - _comment: Mute Hi Conga - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 63 - _comment: Open Hi Conga - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 64 - _comment: Low Conga - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 65 - _comment: High Timbale - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 66 - _comment: Low Timbale - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 67 - _comment: High Agogo - start_freq: 800 - start_vol: 900 - steps: - - { waveform: square_40, target_freq: 800, target_vol: 600, duration: 30 } - - { waveform: square_40, target_freq: 800, target_vol: 0, duration: 120 } - - note: 68 - _comment: Low Agogo - start_freq: 600 - start_vol: 900 - steps: - - { waveform: square_40, target_freq: 600, target_vol: 600, duration: 30 } - - { waveform: square_40, target_freq: 600, target_vol: 0, duration: 120 } - - note: 69 - _comment: Cabasa - start_freq: 6000 - start_vol: 750 - steps: - - { waveform: noise, target_freq: 5000, target_vol: 200, duration: 25 } - - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 35 } - - note: 70 - _comment: Maracas - start_freq: 6000 - start_vol: 750 - steps: - - { waveform: noise, target_freq: 5000, target_vol: 200, duration: 25 } - - { waveform: noise, target_freq: 4000, target_vol: 0, duration: 35 } - - note: 71 - _comment: Short Whistle - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 72 - _comment: Long Whistle - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 73 - _comment: Short Guiro - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 74 - _comment: Long Guiro - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 75 - _comment: Claves - start_freq: 500 - start_vol: 900 - steps: - - { waveform: triangle, target_freq: 500, target_vol: 400, duration: 15 } - - { waveform: triangle, target_freq: 400, target_vol: 0, duration: 20 } - - note: 76 - _comment: Hi Wood Block - start_freq: 800 - start_vol: 900 - steps: - - { waveform: triangle, target_freq: 800, target_vol: 400, duration: 15 } - - { waveform: triangle, target_freq: 700, target_vol: 0, duration: 20 } - - note: 77 - _comment: Low Wood Block - start_freq: 500 - start_vol: 900 - steps: - - { waveform: triangle, target_freq: 500, target_vol: 400, duration: 15 } - - { waveform: triangle, target_freq: 400, target_vol: 0, duration: 20 } - - note: 78 - _comment: Mute Cuica - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 79 - _comment: Open Cuica - start_freq: 300 - start_vol: 850 - steps: - - { waveform: triangle, target_freq: 200, target_vol: 400, duration: 30 } - - { waveform: triangle, target_freq: 150, target_vol: 0, duration: 50 } - - note: 80 - _comment: Mute Triangle - start_freq: 3000 - start_vol: 700 - steps: - - { waveform: sine, target_freq: 3000, target_vol: 400, duration: 15 } - - { waveform: sine, target_freq: 2950, target_vol: 0, duration: 35 } - - note: 81 - _comment: Open Triangle - start_freq: 3000 - start_vol: 700 - steps: - - { waveform: sine, target_freq: 3000, target_vol: 400, duration: 75 } - - { waveform: sine, target_freq: 2950, target_vol: 0, duration: 175 } diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py index 7e0ebf8..3e217ae 100644 --- a/src/converter/midi_to_song.py +++ b/src/converter/midi_to_song.py @@ -507,17 +507,17 @@ def octave_offset_work(octave: int) -> bool: return ((((octave - 2) * 12) <= lowest_note) and (highest_note <= ((octave - 2) * 12 + 63))) - # does ANY octave offset from [2, 7] work? - if any([octave_offset_work(o) for o in range(2, 8)]): + # does ANY octave offset from [0, 9] work? + if any([octave_offset_work(o) for o in range(0, 10)]): # we don't need to modify, when constructing the MakeCode Arcade Tracks, # we'll find the correct octave offset again new_tracks.append(old_track) tracks_that_fit += 1 else: - # split into two tracks, using octave offsets 2 and 7 guarantee covering the - # full MIDI range - low_track = [n for n in old_track if n.note < 64] - high_track = [n for n in old_track if n.note >= 64] + # split into two tracks, using octave offsets 0 and 6 guarantee covering the + # full shifted MIDI range + low_track = [n for n in old_track if n.note < 41] + high_track = [n for n in old_track if n.note >= 41] new_tracks.append(low_track) new_tracks.append(high_track) tracks_that_split += 1 @@ -674,7 +674,7 @@ def octave_offset_work(octave: int) -> bool: return ((((octave - 2) * 12) <= lowest_note) and (highest_note <= ((octave - 2) * 12 + 63))) - if not any([octave_offset_work(o) for o in range(2, 8)]): + if not any([octave_offset_work(o) for o in range(0, 10)]): raise ValueError(f"Track range too big to fit! (please report)") # Chord must have less than 256 notes max_chord_polyphony = max([len(chord.notes) for chord in track]) @@ -734,6 +734,17 @@ def convert_midi_to_song(midi_song: MidiFile, global_timeline) global_timeline: List[AbsoluteCompleteNote] = timeline_group_messages( global_timeline) + + # MIDI file with C4 (MIDI 60) plays at B5 (MIDI 83) + # This is because MakeCode Arcade defines C4 as 49 instead of 60 + # And now I have no idea why I need to shift down another octave but then it works + # Drums don't need this because we already map from MIDI drum notes to an index into + # a list of drum instruments in a track, which we control + for note in global_timeline: + if not note.is_drum: + note.note -= 11 # MIDI 60 (C4) maps to Arcade's C4 of 49 + note.note -= 12 # another octave down makes it correct + global_timeline: List[ AbsoluteCompleteNoteWithTick] = timeline_quantize_to_song_ticks(global_timeline, song) @@ -791,7 +802,7 @@ def octave_offset_work(octave: int) -> bool: return ((((octave - 2) * 12) <= lowest_note) and (highest_note <= ((octave - 2) * 12 + 63))) - for potential_offset in range(2, 8): # find the first offset that works + for potential_offset in range(0, 10): # find the first offset that works if octave_offset_work(potential_offset): instrument.octave = potential_offset break From b0a2628f14fd33d85f362d412ceb942c60035746 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 31 May 2026 19:49:26 -0400 Subject: [PATCH 17/22] Can write to file for output now --- example/instrument_params_3.yaml | 1423 ------------------------------ src/main.py | 24 +- 2 files changed, 17 insertions(+), 1430 deletions(-) delete mode 100644 example/instrument_params_3.yaml diff --git a/example/instrument_params_3.yaml b/example/instrument_params_3.yaml deleted file mode 100644 index 75e44f8..0000000 --- a/example/instrument_params_3.yaml +++ /dev/null @@ -1,1423 +0,0 @@ -_comment: " -This file was generated by Claude for testing purposes. - -Units: - Attack, decay, release: milliseconds - Sustain and amplitudes: range 0-1024 - LFO frequencies: hz - -Waveforms map to as expected by MakeCode Arcade: - triangle = 1 - sawtooth = 2 - sine = 3 - noise_tunable = 4 - noise = 5 - square_10 = 11 - square_20 = 12 - square_30 = 13 - square_40 = 14 - square_50 (or square) = 15 - cycle_16 = 16 - cycle_32 = 17 - cycle_64 (or noise_tunable_2) = 18 - -https://arcade.makecode.com/developer/sound -> The noise is an actual white noise. It ignores the requested frequency or -> note. -> -> The tunable noise tone contains a pseudorandom sample. At high frequency it -> sounds similar to white noise, at low frequency it produces irregular -> crackling noises. -> -> The cycle 16, cycle 32, and cycle 64 waveforms are repeating pseudorandom -> patterns with a cycle length of 16/32/64 samples respectively. They sound -> like a noise-distorted square wave, with short cycles being more regular and -> long cycles being noisier. -" -melodic_instruments: - - instrument: 0 - _comment: Acoustic Grand Piano - waveform: triangle - amp_envelope: { attack: 5, decay: 400, sustain: 300, release: 200, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 1 - _comment: Bright Acoustic Piano - waveform: triangle - amp_envelope: { attack: 2, decay: 350, sustain: 250, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 2 - _comment: Electric Grand Piano - waveform: triangle - amp_envelope: { attack: 5, decay: 450, sustain: 350, release: 200, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 3 - _comment: Honky-tonk Piano - waveform: square_50 - amp_envelope: { attack: 3, decay: 350, sustain: 280, release: 150, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 5, amplitude: 10 } - - instrument: 4 - _comment: Electric Piano 1 - waveform: sine - amp_envelope: { attack: 8, decay: 500, sustain: 400, release: 300, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 5 - _comment: Electric Piano 2 - waveform: sine - amp_envelope: { attack: 8, decay: 480, sustain: 380, release: 280, amplitude: 840 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 6 - _comment: Harpsichord - waveform: sawtooth - amp_envelope: { attack: 1, decay: 200, sustain: 0, release: 50, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 7 - _comment: Clavinet - waveform: square_50 - amp_envelope: { attack: 1, decay: 150, sustain: 0, release: 80, amplitude: 920 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 8 - _comment: Celesta - waveform: triangle - amp_envelope: { attack: 2, decay: 300, sustain: 100, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 9 - _comment: Glockenspiel - waveform: triangle - amp_envelope: { attack: 1, decay: 350, sustain: 100, release: 150, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 10 - _comment: Music Box - waveform: triangle - amp_envelope: { attack: 2, decay: 400, sustain: 150, release: 200, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 11 - _comment: Vibraphone - waveform: sine - amp_envelope: { attack: 20, decay: 600, sustain: 400, release: 400, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 50, amplitude: 30 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 12 - _comment: Marimba - waveform: triangle - amp_envelope: { attack: 2, decay: 300, sustain: 100, release: 150, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 13 - _comment: Xylophone - waveform: triangle - amp_envelope: { attack: 1, decay: 250, sustain: 50, release: 100, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 14 - _comment: Tubular Bells - waveform: sawtooth - amp_envelope: { attack: 5, decay: 800, sustain: 500, release: 500, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 15 - _comment: Dulcimer - waveform: triangle - amp_envelope: { attack: 3, decay: 400, sustain: 250, release: 200, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 16 - _comment: Drawbar Organ - waveform: square_50 - amp_envelope: { attack: 10, decay: 0, sustain: 900, release: 20, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 17 - _comment: Percussive Organ - waveform: square_50 - amp_envelope: { attack: 1, decay: 400, sustain: 0, release: 50, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 18 - _comment: Rock Organ - waveform: square_50 - amp_envelope: { attack: 5, decay: 0, sustain: 880, release: 30, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 19 - _comment: Church Organ - waveform: square_30 - amp_envelope: { attack: 40, decay: 0, sustain: 900, release: 100, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 20 - _comment: Reed Organ - waveform: sawtooth - amp_envelope: { attack: 30, decay: 0, sustain: 850, release: 80, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 21 - _comment: Accordion - waveform: sawtooth - amp_envelope: { attack: 30, decay: 0, sustain: 860, release: 80, amplitude: 810 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 22 - _comment: Harmonica - waveform: square_30 - amp_envelope: { attack: 20, decay: 0, sustain: 840, release: 60, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 23 - _comment: Tango Accordion - waveform: sawtooth - amp_envelope: { attack: 25, decay: 0, sustain: 850, release: 70, amplitude: 810 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 24 - _comment: Acoustic Guitar (nylon) - waveform: triangle - amp_envelope: { attack: 3, decay: 400, sustain: 200, release: 250, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 25 - _comment: Acoustic Guitar (steel) - waveform: triangle - amp_envelope: { attack: 2, decay: 380, sustain: 180, release: 220, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 26 - _comment: Electric Guitar (jazz) - waveform: triangle - amp_envelope: { attack: 5, decay: 450, sustain: 300, release: 200, amplitude: 830 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 27 - _comment: Electric Guitar (clean) - waveform: square_20 - amp_envelope: { attack: 2, decay: 350, sustain: 200, release: 180, amplitude: 860 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 28 - _comment: Electric Guitar (muted) - waveform: square_10 - amp_envelope: { attack: 1, decay: 80, sustain: 0, release: 40, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 29 - _comment: Overdriven Guitar - waveform: sawtooth - amp_envelope: { attack: 5, decay: 200, sustain: 700, release: 150, amplitude: 920 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 30 - _comment: Distortion Guitar - waveform: sawtooth - amp_envelope: { attack: 3, decay: 200, sustain: 750, release: 120, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 31 - _comment: Guitar Harmonics - waveform: triangle - amp_envelope: { attack: 10, decay: 500, sustain: 300, release: 300, amplitude: 780 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 32 - _comment: Acoustic Bass - waveform: triangle - amp_envelope: { attack: 5, decay: 300, sustain: 200, release: 150, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 33 - _comment: Electric Bass (finger) - waveform: triangle - amp_envelope: { attack: 5, decay: 350, sustain: 250, release: 180, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 34 - _comment: Electric Bass (pick) - waveform: sawtooth - amp_envelope: { attack: 2, decay: 300, sustain: 200, release: 150, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 35 - _comment: Fretless Bass - waveform: triangle - amp_envelope: { attack: 10, decay: 400, sustain: 350, release: 250, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 20, amplitude: 8 } - - instrument: 36 - _comment: Slap Bass 1 - waveform: square_50 - amp_envelope: { attack: 1, decay: 200, sustain: 100, release: 100, amplitude: 930 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 37 - _comment: Slap Bass 2 - waveform: square_50 - amp_envelope: { attack: 1, decay: 200, sustain: 100, release: 100, amplitude: 940 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 38 - _comment: Synth Bass 1 - waveform: sawtooth - amp_envelope: { attack: 5, decay: 300, sustain: 400, release: 200, amplitude: 920 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 39 - _comment: Synth Bass 2 - waveform: square_30 - amp_envelope: { attack: 8, decay: 350, sustain: 450, release: 250, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 40 - _comment: Violin - waveform: sawtooth - amp_envelope: { attack: 60, decay: 0, sustain: 800, release: 100, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 50, amplitude: 15 } - - instrument: 41 - _comment: Viola - waveform: sawtooth - amp_envelope: { attack: 70, decay: 0, sustain: 800, release: 120, amplitude: 810 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 50, amplitude: 15 } - - instrument: 42 - _comment: Cello - waveform: sawtooth - amp_envelope: { attack: 80, decay: 0, sustain: 800, release: 150, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 45, amplitude: 15 } - - instrument: 43 - _comment: Contrabass - waveform: sawtooth - amp_envelope: { attack: 100, decay: 0, sustain: 800, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 40, amplitude: 10 } - - instrument: 44 - _comment: Tremolo Strings - waveform: triangle - amp_envelope: { attack: 50, decay: 0, sustain: 750, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 80, amplitude: 200 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 45 - _comment: Pizzicato Strings - waveform: triangle - amp_envelope: { attack: 1, decay: 200, sustain: 0, release: 100, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 46 - _comment: Orchestral Harp - waveform: triangle - amp_envelope: { attack: 2, decay: 500, sustain: 300, release: 300, amplitude: 840 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 47 - _comment: Timpani - waveform: noise_tunable - amp_envelope: { attack: 5, decay: 400, sustain: 200, release: 300, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 48 - _comment: String Ensemble 1 - waveform: sawtooth - amp_envelope: { attack: 80, decay: 0, sustain: 800, release: 200, amplitude: 810 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 49 - _comment: String Ensemble 2 - waveform: sawtooth - amp_envelope: { attack: 100, decay: 0, sustain: 800, release: 250, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 50 - _comment: Synth Strings 1 - waveform: sawtooth - amp_envelope: { attack: 60, decay: 0, sustain: 800, release: 200, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 30, amplitude: 10 } - - instrument: 51 - _comment: Synth Strings 2 - waveform: sawtooth - amp_envelope: { attack: 50, decay: 0, sustain: 800, release: 180, amplitude: 830 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 52 - _comment: Choir Aahs - waveform: sine - amp_envelope: { attack: 80, decay: 0, sustain: 800, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 53 - _comment: Voice Oohs - waveform: sine - amp_envelope: { attack: 100, decay: 0, sustain: 800, release: 250, amplitude: 790 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 54 - _comment: Synth Voice - waveform: sine - amp_envelope: { attack: 60, decay: 0, sustain: 800, release: 200, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 30, amplitude: 12 } - - instrument: 55 - _comment: Orchestra Hit - waveform: sawtooth - amp_envelope: { attack: 1, decay: 200, sustain: 0, release: 100, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 56 - _comment: Trumpet - waveform: sawtooth - amp_envelope: { attack: 20, decay: 0, sustain: 820, release: 80, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 57 - _comment: Trombone - waveform: sawtooth - amp_envelope: { attack: 30, decay: 0, sustain: 820, release: 100, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 58 - _comment: Tuba - waveform: sawtooth - amp_envelope: { attack: 40, decay: 0, sustain: 800, release: 120, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 59 - _comment: Muted Trumpet - waveform: square_20 - amp_envelope: { attack: 15, decay: 0, sustain: 780, release: 60, amplitude: 860 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 60 - _comment: French Horn - waveform: triangle - amp_envelope: { attack: 50, decay: 0, sustain: 800, release: 150, amplitude: 860 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 61 - _comment: Brass Section - waveform: sawtooth - amp_envelope: { attack: 25, decay: 0, sustain: 820, release: 100, amplitude: 890 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 62 - _comment: Synth Brass 1 - waveform: sawtooth - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 80, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 63 - _comment: Synth Brass 2 - waveform: square_40 - amp_envelope: { attack: 10, decay: 0, sustain: 800, release: 60, amplitude: 910 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 64 - _comment: Soprano Sax - waveform: sawtooth - amp_envelope: { attack: 15, decay: 0, sustain: 820, release: 80, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 65 - _comment: Alto Sax - waveform: sawtooth - amp_envelope: { attack: 20, decay: 0, sustain: 820, release: 90, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 66 - _comment: Tenor Sax - waveform: sawtooth - amp_envelope: { attack: 25, decay: 0, sustain: 820, release: 100, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 67 - _comment: Baritone Sax - waveform: sawtooth - amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 120, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 68 - _comment: Oboe - waveform: square_30 - amp_envelope: { attack: 20, decay: 0, sustain: 820, release: 80, amplitude: 840 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 69 - _comment: English Horn - waveform: square_30 - amp_envelope: { attack: 25, decay: 0, sustain: 820, release: 90, amplitude: 830 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 70 - _comment: Bassoon - waveform: sawtooth - amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 100, amplitude: 830 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 71 - _comment: Clarinet - waveform: square_30 - amp_envelope: { attack: 20, decay: 0, sustain: 830, release: 80, amplitude: 840 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 72 - _comment: Piccolo - waveform: triangle - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 60, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 73 - _comment: Flute - waveform: sine - amp_envelope: { attack: 20, decay: 0, sustain: 800, release: 80, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 40, amplitude: 8 } - - instrument: 74 - _comment: Recorder - waveform: triangle - amp_envelope: { attack: 15, decay: 0, sustain: 800, release: 70, amplitude: 810 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 75 - _comment: Pan Flute - waveform: sine - amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 76 - _comment: Blown Bottle - waveform: sine - amp_envelope: { attack: 25, decay: 0, sustain: 750, release: 100, amplitude: 790 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 77 - _comment: Shakuhachi - waveform: triangle - amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 25, amplitude: 12 } - - instrument: 78 - _comment: Whistle - waveform: sine - amp_envelope: { attack: 10, decay: 0, sustain: 800, release: 60, amplitude: 810 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 79 - _comment: Ocarina - waveform: sine - amp_envelope: { attack: 20, decay: 0, sustain: 810, release: 80, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 80 - _comment: Lead 1 (square) - waveform: square_50 - amp_envelope: { attack: 5, decay: 0, sustain: 800, release: 60, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 81 - _comment: Lead 2 (sawtooth) - waveform: sawtooth - amp_envelope: { attack: 3, decay: 0, sustain: 800, release: 50, amplitude: 920 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 82 - _comment: Lead 3 (calliope) - waveform: triangle - amp_envelope: { attack: 10, decay: 0, sustain: 800, release: 80, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 83 - _comment: Lead 4 (chiff) - waveform: noise_tunable - amp_envelope: { attack: 2, decay: 300, sustain: 400, release: 200, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 84 - _comment: Lead 5 (charang) - waveform: sawtooth - amp_envelope: { attack: 5, decay: 0, sustain: 800, release: 60, amplitude: 910 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 85 - _comment: Lead 6 (voice) - waveform: sine - amp_envelope: { attack: 30, decay: 0, sustain: 800, release: 100, amplitude: 840 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 86 - _comment: Lead 7 (fifths) - waveform: square_50 - amp_envelope: { attack: 5, decay: 0, sustain: 800, release: 60, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 87 - _comment: Lead 8 (bass+lead) - waveform: sawtooth - amp_envelope: { attack: 5, decay: 0, sustain: 800, release: 60, amplitude: 930 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 88 - _comment: Pad 1 (new age) - waveform: triangle - amp_envelope: { attack: 200, decay: 0, sustain: 800, release: 400, amplitude: 780 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 89 - _comment: Pad 2 (warm) - waveform: sine - amp_envelope: { attack: 150, decay: 0, sustain: 800, release: 350, amplitude: 770 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 90 - _comment: Pad 3 (polysynth) - waveform: sawtooth - amp_envelope: { attack: 100, decay: 0, sustain: 800, release: 300, amplitude: 790 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 91 - _comment: Pad 4 (choir) - waveform: sine - amp_envelope: { attack: 200, decay: 0, sustain: 800, release: 400, amplitude: 770 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 92 - _comment: Pad 5 (bowed) - waveform: sawtooth - amp_envelope: { attack: 180, decay: 0, sustain: 800, release: 400, amplitude: 780 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 93 - _comment: Pad 6 (metallic) - waveform: cycle_32 - amp_envelope: { attack: 100, decay: 0, sustain: 750, release: 300, amplitude: 780 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 94 - _comment: Pad 7 (halo) - waveform: triangle - amp_envelope: { attack: 200, decay: 0, sustain: 800, release: 400, amplitude: 760 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 95 - _comment: Pad 8 (sweep) - waveform: sawtooth - amp_envelope: { attack: 150, decay: 0, sustain: 800, release: 400, amplitude: 780 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 96 - _comment: FX 1 (rain) - waveform: noise_tunable - amp_envelope: { attack: 50, decay: 0, sustain: 700, release: 200, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 97 - _comment: FX 2 (soundtrack) - waveform: triangle - amp_envelope: { attack: 200, decay: 0, sustain: 800, release: 400, amplitude: 760 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 98 - _comment: FX 3 (crystal) - waveform: triangle - amp_envelope: { attack: 5, decay: 500, sustain: 300, release: 400, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 99 - _comment: FX 4 (atmosphere) - waveform: sine - amp_envelope: { attack: 200, decay: 0, sustain: 750, release: 500, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 100 - _comment: FX 5 (brightness) - waveform: triangle - amp_envelope: { attack: 10, decay: 0, sustain: 800, release: 200, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 101 - _comment: FX 6 (goblins) - waveform: cycle_32 - amp_envelope: { attack: 100, decay: 0, sustain: 750, release: 300, amplitude: 780 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 102 - _comment: FX 7 (echoes) - waveform: sine - amp_envelope: { attack: 50, decay: 400, sustain: 500, release: 500, amplitude: 760 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 103 - _comment: FX 8 (sci-fi) - waveform: cycle_64 - amp_envelope: { attack: 100, decay: 0, sustain: 750, release: 300, amplitude: 770 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 104 - _comment: Sitar - waveform: sawtooth - amp_envelope: { attack: 5, decay: 500, sustain: 350, release: 300, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 25, amplitude: 15 } - - instrument: 105 - _comment: Banjo - waveform: triangle - amp_envelope: { attack: 2, decay: 350, sustain: 150, release: 200, amplitude: 870 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 106 - _comment: Shamisen - waveform: sawtooth - amp_envelope: { attack: 2, decay: 300, sustain: 100, release: 150, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 107 - _comment: Koto - waveform: triangle - amp_envelope: { attack: 3, decay: 400, sustain: 200, release: 250, amplitude: 860 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 108 - _comment: Kalimba - waveform: triangle - amp_envelope: { attack: 1, decay: 350, sustain: 100, release: 200, amplitude: 840 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 109 - _comment: Bag Pipe - waveform: square_30 - amp_envelope: { attack: 30, decay: 0, sustain: 820, release: 80, amplitude: 840 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 110 - _comment: Fiddle - waveform: sawtooth - amp_envelope: { attack: 40, decay: 0, sustain: 800, release: 100, amplitude: 840 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 50, amplitude: 15 } - - instrument: 111 - _comment: Shanai - waveform: sawtooth - amp_envelope: { attack: 20, decay: 0, sustain: 820, release: 80, amplitude: 840 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 112 - _comment: Tinkle Bell - waveform: triangle - amp_envelope: { attack: 1, decay: 400, sustain: 200, release: 300, amplitude: 820 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 113 - _comment: Agogo - waveform: square_50 - amp_envelope: { attack: 1, decay: 200, sustain: 50, release: 100, amplitude: 880 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 114 - _comment: Steel Drums - waveform: triangle - amp_envelope: { attack: 2, decay: 350, sustain: 200, release: 250, amplitude: 860 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 115 - _comment: Woodblock - waveform: square_50 - amp_envelope: { attack: 1, decay: 80, sustain: 0, release: 40, amplitude: 920 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 116 - _comment: Taiko Drum - waveform: noise_tunable - amp_envelope: { attack: 2, decay: 300, sustain: 0, release: 150, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 117 - _comment: Melodic Tom - waveform: noise_tunable - amp_envelope: { attack: 2, decay: 350, sustain: 0, release: 200, amplitude: 930 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 118 - _comment: Synth Drum - waveform: noise - amp_envelope: { attack: 2, decay: 250, sustain: 0, release: 120, amplitude: 900 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 119 - _comment: Reverse Cymbal - waveform: noise - amp_envelope: { attack: 500, decay: 0, sustain: 800, release: 100, amplitude: 800 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 120 - _comment: Guitar Fret Noise - waveform: noise - amp_envelope: { attack: 1, decay: 100, sustain: 0, release: 50, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 121 - _comment: Breath Noise - waveform: noise - amp_envelope: { attack: 5, decay: 200, sustain: 300, release: 200, amplitude: 650 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 122 - _comment: Seashore - waveform: noise - amp_envelope: { attack: 200, decay: 0, sustain: 700, release: 400, amplitude: 650 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 123 - _comment: Bird Tweet - waveform: triangle - amp_envelope: { attack: 5, decay: 200, sustain: 400, release: 200, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 100, amplitude: 50 } - - instrument: 124 - _comment: Telephone Ring - waveform: square_50 - amp_envelope: { attack: 1, decay: 0, sustain: 800, release: 50, amplitude: 850 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 40, amplitude: 500 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 125 - _comment: Helicopter - waveform: noise_tunable - amp_envelope: { attack: 100, decay: 0, sustain: 700, release: 200, amplitude: 750 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 126 - _comment: Applause - waveform: noise - amp_envelope: { attack: 100, decay: 0, sustain: 700, release: 400, amplitude: 700 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - - instrument: 127 - _comment: Gunshot - waveform: noise - amp_envelope: { attack: 1, decay: 200, sustain: 0, release: 100, amplitude: 950 } - pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } - amp_lfo: { frequency: 0, amplitude: 0 } - pitch_lfo: { frequency: 0, amplitude: 0 } - -drum_instruments: - - note: 27 - _comment: High Q - start_freq: 3500 - start_vol: 900 - steps: - - { waveform: cycle_16 , target_freq: 2800, target_vol: 500, duration: 5 } - - { waveform: cycle_16 , target_freq: 2000, target_vol: 0, duration: 8 } - - note: 28 - _comment: Slap - start_freq: 900 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 600, target_vol: 400, duration: 4 } - - { waveform: noise_tunable , target_freq: 300, target_vol: 0, duration: 6 } - - note: 29 - _comment: Scratch Push - start_freq: 300 - start_vol: 750 - steps: - - { waveform: noise_tunable , target_freq: 800, target_vol: 600, duration: 6 } - - { waveform: noise_tunable , target_freq: 1500, target_vol: 0, duration: 8 } - - note: 30 - _comment: Scratch Pull - start_freq: 1500 - start_vol: 750 - steps: - - { waveform: noise_tunable , target_freq: 800, target_vol: 500, duration: 6 } - - { waveform: noise_tunable , target_freq: 200, target_vol: 0, duration: 10 } - - note: 31 - _comment: Sticks - start_freq: 1800 - start_vol: 880 - steps: - - { waveform: square_50 , target_freq: 1600, target_vol: 400, duration: 3 } - - { waveform: square_50 , target_freq: 1200, target_vol: 0, duration: 4 } - - note: 32 - _comment: Square Click - start_freq: 1500 - start_vol: 900 - steps: - - { waveform: square_50 , target_freq: 1200, target_vol: 400, duration: 3 } - - { waveform: square_50 , target_freq: 800, target_vol: 0, duration: 4 } - - note: 33 - _comment: Metronome Click - start_freq: 2000 - start_vol: 900 - steps: - - { waveform: square_50 , target_freq: 1600, target_vol: 500, duration: 3 } - - { waveform: square_50 , target_freq: 1000, target_vol: 0, duration: 4 } - - note: 34 - _comment: Metronome Bell - start_freq: 2200 - start_vol: 860 - steps: - - { waveform: triangle , target_freq: 2000, target_vol: 600, duration: 5 } - - { waveform: triangle , target_freq: 1800, target_vol: 300, duration: 8 } - - { waveform: triangle , target_freq: 1500, target_vol: 0, duration: 12 } - - note: 35 - _comment: Acoustic Bass Drum - start_freq: 180 - start_vol: 950 - steps: - - { waveform: noise_tunable , target_freq: 100, target_vol: 700, duration: 5 } - - { waveform: noise_tunable , target_freq: 60, target_vol: 300, duration: 8 } - - { waveform: noise_tunable , target_freq: 40, target_vol: 0, duration: 12 } - - note: 36 - _comment: Bass Drum 1 - start_freq: 160 - start_vol: 950 - steps: - - { waveform: noise_tunable , target_freq: 90, target_vol: 700, duration: 5 } - - { waveform: noise_tunable , target_freq: 50, target_vol: 300, duration: 8 } - - { waveform: noise_tunable , target_freq: 35, target_vol: 0, duration: 10 } - - note: 37 - _comment: Side Stick - start_freq: 900 - start_vol: 920 - steps: - - { waveform: noise , target_freq: 700, target_vol: 500, duration: 3 } - - { waveform: noise , target_freq: 500, target_vol: 0, duration: 5 } - - note: 38 - _comment: Acoustic Snare - start_freq: 400 - start_vol: 940 - steps: - - { waveform: noise , target_freq: 300, target_vol: 600, duration: 5 } - - { waveform: noise , target_freq: 200, target_vol: 200, duration: 8 } - - { waveform: noise , target_freq: 100, target_vol: 0, duration: 10 } - - note: 39 - _comment: Hand Clap - start_freq: 1200 - start_vol: 920 - steps: - - { waveform: noise , target_freq: 900, target_vol: 500, duration: 4 } - - { waveform: noise , target_freq: 600, target_vol: 200, duration: 6 } - - { waveform: noise , target_freq: 300, target_vol: 0, duration: 8 } - - note: 40 - _comment: Electric Snare - start_freq: 350 - start_vol: 930 - steps: - - { waveform: noise , target_freq: 250, target_vol: 600, duration: 5 } - - { waveform: noise , target_freq: 150, target_vol: 200, duration: 7 } - - { waveform: noise , target_freq: 80, target_vol: 0, duration: 9 } - - note: 41 - _comment: Low Floor Tom - start_freq: 100 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 70, target_vol: 600, duration: 7 } - - { waveform: noise_tunable , target_freq: 50, target_vol: 200, duration: 10 } - - { waveform: noise_tunable , target_freq: 35, target_vol: 0, duration: 15 } - - note: 42 - _comment: Closed Hi-Hat - start_freq: 4000 - start_vol: 860 - steps: - - { waveform: noise , target_freq: 3500, target_vol: 400, duration: 3 } - - { waveform: noise , target_freq: 3000, target_vol: 0, duration: 4 } - - note: 43 - _comment: High Floor Tom - start_freq: 130 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 90, target_vol: 600, duration: 7 } - - { waveform: noise_tunable , target_freq: 65, target_vol: 200, duration: 10 } - - { waveform: noise_tunable , target_freq: 45, target_vol: 0, duration: 14 } - - note: 44 - _comment: Pedal Hi-Hat - start_freq: 3500 - start_vol: 800 - steps: - - { waveform: noise , target_freq: 3000, target_vol: 400, duration: 3 } - - { waveform: noise , target_freq: 2500, target_vol: 100, duration: 5 } - - { waveform: noise , target_freq: 2000, target_vol: 0, duration: 6 } - - note: 45 - _comment: Low Tom - start_freq: 150 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 110, target_vol: 600, duration: 7 } - - { waveform: noise_tunable , target_freq: 75, target_vol: 200, duration: 10 } - - { waveform: noise_tunable , target_freq: 50, target_vol: 0, duration: 14 } - - note: 46 - _comment: Open Hi-Hat - start_freq: 4000 - start_vol: 860 - steps: - - { waveform: noise , target_freq: 3800, target_vol: 700, duration: 8 } - - { waveform: noise , target_freq: 3500, target_vol: 400, duration: 14 } - - { waveform: noise , target_freq: 3000, target_vol: 100, duration: 20 } - - { waveform: noise , target_freq: 2500, target_vol: 0, duration: 22 } - - note: 47 - _comment: Low-Mid Tom - start_freq: 180 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 130, target_vol: 600, duration: 7 } - - { waveform: noise_tunable , target_freq: 90, target_vol: 200, duration: 10 } - - { waveform: noise_tunable , target_freq: 60, target_vol: 0, duration: 13 } - - note: 48 - _comment: Hi-Mid Tom - start_freq: 250 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 180, target_vol: 600, duration: 6 } - - { waveform: noise_tunable , target_freq: 120, target_vol: 200, duration: 9 } - - { waveform: noise_tunable , target_freq: 80, target_vol: 0, duration: 12 } - - note: 49 - _comment: Crash Cymbal 1 - start_freq: 5000 - start_vol: 920 - steps: - - { waveform: noise , target_freq: 4500, target_vol: 700, duration: 10 } - - { waveform: noise , target_freq: 4000, target_vol: 400, duration: 18 } - - { waveform: noise , target_freq: 3000, target_vol: 100, duration: 25 } - - { waveform: noise , target_freq: 2000, target_vol: 0, duration: 30 } - - note: 50 - _comment: High Tom - start_freq: 350 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 250, target_vol: 600, duration: 5 } - - { waveform: noise_tunable , target_freq: 160, target_vol: 200, duration: 8 } - - { waveform: noise_tunable , target_freq: 100, target_vol: 0, duration: 11 } - - note: 51 - _comment: Ride Cymbal 1 - start_freq: 4500 - start_vol: 820 - steps: - - { waveform: noise , target_freq: 4200, target_vol: 600, duration: 8 } - - { waveform: noise , target_freq: 4000, target_vol: 350, duration: 15 } - - { waveform: noise , target_freq: 3500, target_vol: 100, duration: 22 } - - { waveform: noise , target_freq: 3000, target_vol: 0, duration: 28 } - - note: 52 - _comment: Chinese Cymbal - start_freq: 5500 - start_vol: 920 - steps: - - { waveform: cycle_64 , target_freq: 5000, target_vol: 700, duration: 8 } - - { waveform: cycle_64 , target_freq: 4500, target_vol: 400, duration: 15 } - - { waveform: cycle_64 , target_freq: 4000, target_vol: 100, duration: 22 } - - { waveform: cycle_64 , target_freq: 3000, target_vol: 0, duration: 28 } - - note: 53 - _comment: Ride Bell - start_freq: 2800 - start_vol: 870 - steps: - - { waveform: triangle , target_freq: 2600, target_vol: 600, duration: 6 } - - { waveform: triangle , target_freq: 2400, target_vol: 300, duration: 12 } - - { waveform: triangle , target_freq: 2200, target_vol: 100, duration: 20 } - - { waveform: triangle , target_freq: 2000, target_vol: 0, duration: 25 } - - note: 54 - _comment: Tambourine - start_freq: 4500 - start_vol: 800 - steps: - - { waveform: noise , target_freq: 4000, target_vol: 500, duration: 5 } - - { waveform: noise , target_freq: 3500, target_vol: 200, duration: 8 } - - { waveform: noise , target_freq: 3000, target_vol: 0, duration: 10 } - - note: 55 - _comment: Splash Cymbal - start_freq: 5500 - start_vol: 890 - steps: - - { waveform: noise , target_freq: 5000, target_vol: 600, duration: 7 } - - { waveform: noise , target_freq: 4500, target_vol: 300, duration: 12 } - - { waveform: noise , target_freq: 4000, target_vol: 0, duration: 16 } - - note: 56 - _comment: Cowbell - start_freq: 600 - start_vol: 910 - steps: - - { waveform: square_50 , target_freq: 580, target_vol: 700, duration: 6 } - - { waveform: square_50 , target_freq: 550, target_vol: 400, duration: 12 } - - { waveform: square_50 , target_freq: 520, target_vol: 100, duration: 18 } - - { waveform: square_50 , target_freq: 500, target_vol: 0, duration: 22 } - - note: 57 - _comment: Crash Cymbal 2 - start_freq: 4800 - start_vol: 920 - steps: - - { waveform: noise , target_freq: 4400, target_vol: 700, duration: 10 } - - { waveform: noise , target_freq: 3900, target_vol: 400, duration: 18 } - - { waveform: noise , target_freq: 3000, target_vol: 100, duration: 25 } - - { waveform: noise , target_freq: 2200, target_vol: 0, duration: 30 } - - note: 58 - _comment: Vibraslap - start_freq: 800 - start_vol: 820 - steps: - - { waveform: cycle_32 , target_freq: 700, target_vol: 600, duration: 8 } - - { waveform: cycle_32 , target_freq: 600, target_vol: 400, duration: 12 } - - { waveform: cycle_32 , target_freq: 500, target_vol: 200, duration: 16 } - - { waveform: cycle_32 , target_freq: 400, target_vol: 0, duration: 18 } - - note: 59 - _comment: Ride Cymbal 2 - start_freq: 4200 - start_vol: 810 - steps: - - { waveform: noise , target_freq: 4000, target_vol: 600, duration: 8 } - - { waveform: noise , target_freq: 3700, target_vol: 350, duration: 15 } - - { waveform: noise , target_freq: 3200, target_vol: 100, duration: 22 } - - { waveform: noise , target_freq: 2800, target_vol: 0, duration: 27 } - - note: 60 - _comment: Hi Bongo - start_freq: 450 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 350, target_vol: 600, duration: 5 } - - { waveform: noise_tunable , target_freq: 250, target_vol: 200, duration: 8 } - - { waveform: noise_tunable , target_freq: 150, target_vol: 0, duration: 11 } - - note: 61 - _comment: Low Bongo - start_freq: 280 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 210, target_vol: 600, duration: 6 } - - { waveform: noise_tunable , target_freq: 150, target_vol: 200, duration: 9 } - - { waveform: noise_tunable , target_freq: 100, target_vol: 0, duration: 12 } - - note: 62 - _comment: Mute Hi Conga - start_freq: 380 - start_vol: 900 - steps: - - { waveform: noise_tunable , target_freq: 300, target_vol: 500, duration: 4 } - - { waveform: noise_tunable , target_freq: 220, target_vol: 100, duration: 6 } - - { waveform: noise_tunable , target_freq: 150, target_vol: 0, duration: 7 } - - note: 63 - _comment: Open Hi Conga - start_freq: 360 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 300, target_vol: 700, duration: 6 } - - { waveform: noise_tunable , target_freq: 240, target_vol: 300, duration: 10 } - - { waveform: noise_tunable , target_freq: 180, target_vol: 0, duration: 14 } - - note: 64 - _comment: Low Conga - start_freq: 220 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 170, target_vol: 700, duration: 7 } - - { waveform: noise_tunable , target_freq: 120, target_vol: 300, duration: 11 } - - { waveform: noise_tunable , target_freq: 80, target_vol: 0, duration: 15 } - - note: 65 - _comment: High Timbale - start_freq: 550 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 420, target_vol: 600, duration: 5 } - - { waveform: noise_tunable , target_freq: 300, target_vol: 200, duration: 8 } - - { waveform: noise_tunable , target_freq: 200, target_vol: 0, duration: 11 } - - note: 66 - _comment: Low Timbale - start_freq: 380 - start_vol: 920 - steps: - - { waveform: noise_tunable , target_freq: 280, target_vol: 600, duration: 6 } - - { waveform: noise_tunable , target_freq: 200, target_vol: 200, duration: 9 } - - { waveform: noise_tunable , target_freq: 130, target_vol: 0, duration: 12 } - - note: 67 - _comment: High Agogo - start_freq: 800 - start_vol: 880 - steps: - - { waveform: square_50 , target_freq: 750, target_vol: 600, duration: 5 } - - { waveform: square_50 , target_freq: 700, target_vol: 250, duration: 10 } - - { waveform: square_50 , target_freq: 650, target_vol: 0, duration: 14 } - - note: 68 - _comment: Low Agogo - start_freq: 600 - start_vol: 880 - steps: - - { waveform: square_50 , target_freq: 560, target_vol: 600, duration: 6 } - - { waveform: square_50 , target_freq: 520, target_vol: 250, duration: 11 } - - { waveform: square_50 , target_freq: 480, target_vol: 0, duration: 15 } - - note: 69 - _comment: Cabasa - start_freq: 4000 - start_vol: 760 - steps: - - { waveform: noise , target_freq: 3500, target_vol: 400, duration: 5 } - - { waveform: noise , target_freq: 3000, target_vol: 150, duration: 8 } - - { waveform: noise , target_freq: 2500, target_vol: 0, duration: 10 } - - note: 70 - _comment: Maracas - start_freq: 5000 - start_vol: 760 - steps: - - { waveform: noise , target_freq: 4500, target_vol: 400, duration: 4 } - - { waveform: noise , target_freq: 4000, target_vol: 0, duration: 6 } - - note: 71 - _comment: Short Whistle - start_freq: 2000 - start_vol: 820 - steps: - - { waveform: sine , target_freq: 2050, target_vol: 500, duration: 6 } - - { waveform: sine , target_freq: 1900, target_vol: 0, duration: 8 } - - note: 72 - _comment: Long Whistle - start_freq: 1800 - start_vol: 830 - steps: - - { waveform: sine , target_freq: 1850, target_vol: 800, duration: 20 } - - { waveform: sine , target_freq: 1800, target_vol: 400, duration: 20 } - - { waveform: sine , target_freq: 1750, target_vol: 0, duration: 15 } - - note: 73 - _comment: Short Guiro - start_freq: 1200 - start_vol: 750 - steps: - - { waveform: cycle_32 , target_freq: 1000, target_vol: 500, duration: 5 } - - { waveform: cycle_32 , target_freq: 800, target_vol: 200, duration: 7 } - - { waveform: cycle_32 , target_freq: 600, target_vol: 0, duration: 8 } - - note: 74 - _comment: Long Guiro - start_freq: 1500 - start_vol: 750 - steps: - - { waveform: cycle_32 , target_freq: 1200, target_vol: 600, duration: 10 } - - { waveform: cycle_32 , target_freq: 900, target_vol: 350, duration: 15 } - - { waveform: cycle_32 , target_freq: 600, target_vol: 100, duration: 18 } - - { waveform: cycle_32 , target_freq: 400, target_vol: 0, duration: 12 } - - note: 75 - _comment: Claves - start_freq: 1200 - start_vol: 960 - steps: - - { waveform: square_50 , target_freq: 1100, target_vol: 400, duration: 3 } - - { waveform: square_50 , target_freq: 900, target_vol: 0, duration: 4 } - - note: 76 - _comment: Hi Wood Block - start_freq: 900 - start_vol: 940 - steps: - - { waveform: square_50 , target_freq: 800, target_vol: 500, duration: 4 } - - { waveform: square_50 , target_freq: 650, target_vol: 100, duration: 6 } - - { waveform: square_50 , target_freq: 500, target_vol: 0, duration: 7 } - - note: 77 - _comment: Low Wood Block - start_freq: 650 - start_vol: 940 - steps: - - { waveform: square_50 , target_freq: 580, target_vol: 500, duration: 5 } - - { waveform: square_50 , target_freq: 470, target_vol: 100, duration: 7 } - - { waveform: square_50 , target_freq: 380, target_vol: 0, duration: 8 } - - note: 78 - _comment: Mute Cuica - start_freq: 500 - start_vol: 860 - steps: - - { waveform: noise_tunable , target_freq: 550, target_vol: 500, duration: 4 } - - { waveform: noise_tunable , target_freq: 500, target_vol: 0, duration: 5 } - - note: 79 - _comment: Open Cuica - start_freq: 400 - start_vol: 870 - steps: - - { waveform: noise_tunable , target_freq: 500, target_vol: 700, duration: 6 } - - { waveform: noise_tunable , target_freq: 650, target_vol: 500, duration: 8 } - - { waveform: noise_tunable , target_freq: 700, target_vol: 200, duration: 10 } - - { waveform: noise_tunable , target_freq: 600, target_vol: 0, duration: 8 } - - note: 80 - _comment: Mute Triangle - start_freq: 3500 - start_vol: 840 - steps: - - { waveform: triangle , target_freq: 3400, target_vol: 500, duration: 4 } - - { waveform: triangle , target_freq: 3200, target_vol: 0, duration: 5 } - - note: 81 - _comment: Open Triangle - start_freq: 3500 - start_vol: 840 - steps: - - { waveform: triangle , target_freq: 3450, target_vol: 800, duration: 15 } - - { waveform: triangle , target_freq: 3400, target_vol: 600, duration: 25 } - - { waveform: triangle , target_freq: 3300, target_vol: 300, duration: 30 } - - { waveform: triangle , target_freq: 3200, target_vol: 0, duration: 30 } - - note: 82 - _comment: Shaker - start_freq: 5000 - start_vol: 760 - steps: - - { waveform: noise , target_freq: 4500, target_vol: 500, duration: 6 } - - { waveform: noise , target_freq: 4000, target_vol: 200, duration: 9 } - - { waveform: noise , target_freq: 3500, target_vol: 0, duration: 10 } - - note: 83 - _comment: Jingle Bell - start_freq: 2500 - start_vol: 820 - steps: - - { waveform: triangle , target_freq: 2400, target_vol: 600, duration: 8 } - - { waveform: triangle , target_freq: 2300, target_vol: 300, duration: 15 } - - { waveform: triangle , target_freq: 2200, target_vol: 100, duration: 20 } - - { waveform: triangle , target_freq: 2100, target_vol: 0, duration: 20 } - - note: 84 - _comment: Bell Tree - start_freq: 1500 - start_vol: 800 - steps: - - { waveform: triangle , target_freq: 2000, target_vol: 700, duration: 10 } - - { waveform: triangle , target_freq: 2500, target_vol: 500, duration: 15 } - - { waveform: triangle , target_freq: 3000, target_vol: 200, duration: 18 } - - { waveform: triangle , target_freq: 3500, target_vol: 0, duration: 20 } - - note: 85 - _comment: Castanets - start_freq: 1400 - start_vol: 950 - steps: - - { waveform: square_50 , target_freq: 1200, target_vol: 400, duration: 3 } - - { waveform: square_50 , target_freq: 900, target_vol: 0, duration: 4 } - - note: 86 - _comment: Mute Surdo - start_freq: 120 - start_vol: 940 - steps: - - { waveform: noise_tunable , target_freq: 90, target_vol: 600, duration: 6 } - - { waveform: noise_tunable , target_freq: 65, target_vol: 200, duration: 8 } - - { waveform: noise_tunable , target_freq: 50, target_vol: 0, duration: 8 } - - note: 87 - _comment: Open Surdo - start_freq: 100 - start_vol: 950 - steps: - - { waveform: noise_tunable , target_freq: 80, target_vol: 700, duration: 8 } - - { waveform: noise_tunable , target_freq: 60, target_vol: 350, duration: 14 } - - { waveform: noise_tunable , target_freq: 45, target_vol: 100, duration: 18 } - - { waveform: noise_tunable , target_freq: 35, target_vol: 0, duration: 20 } diff --git a/src/main.py b/src/main.py index 718a3b4..ac5b073 100644 --- a/src/main.py +++ b/src/main.py @@ -10,12 +10,14 @@ from utils.logger import create_logger, set_all_stdout_logger_levels parser = ArgumentParser(description="Convert a MIDI file to a MakeCode Arcade song.") -parser.add_argument("--input", "-i", type=Path, +parser.add_argument("--input", "-i", type=Path, required=True, help="Input MIDI file.") parser.add_argument("--input-instrument-params", "-p", type=Path, - help="Input instrument parameter mapping file.") -# parser.add_argument("--output", "-o", type=Path, -# help="Output TypeScript file path, otherwise will write to stdout.") + required=True, help="Input instrument parameter mapping file.") +parser.add_argument("--output", "-o", type=Path, + help="Output TypeScript file path, otherwise will write to stdout. " + "If specified, this will overwrite the file if it already " + "exists, and the parent directories must exist.") parser.add_argument("--debug", action="store_const", const=logging.DEBUG, default=logging.INFO, help="Include debug messages. Defaults to info and " @@ -26,13 +28,13 @@ logger.debug(f"Received arguments: {args}") input_path = Path(args.input) -logger.debug(f"Reading MIDI file from {input_path}") +logger.info(f"Reading MIDI file from {input_path}") mid = MidiFile(input_path) logger.debug(f"Found {len(mid.tracks)} tracks, length of {mid.length}s") input_instrument_param_path = Path(args.input_instrument_params) -logger.debug(f"Reading instrument parameter mapping from {input_instrument_param_path}") +logger.info(f"Reading instrument parameter mapping from {input_instrument_param_path}") mapping = load_instrument_params(input_instrument_param_path.read_text()) logger.debug(f"Mapped {len(mapping.melodic_instruments)} melodic instruments and " @@ -40,5 +42,13 @@ song = convert_midi_to_song(mid, mapping) h = encode_song_to_hex(song) +final_output = f"hex`{h}`" logger.info("Finished converting MIDI file") -print(f"hex`{h}`") + +output_path = Path(args.output) if args.output is not None else None +if output_path is not None: + logger.info(f"Writing result to {output_path}") + output_path.write_text(final_output) +else: + logger.info(f"Writing result to stdout") + print(final_output) From b0a5c8921235f59fd1883337fbf9769816d70d34 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 31 May 2026 20:35:14 -0400 Subject: [PATCH 18/22] Refactor to Python packages --- src/arcade/__init__.py | 0 src/converter/midi_to_song.py | 845 ------------------ src/main.py | 4 +- src/midi_to_song/__init__.py | 162 ++++ .../instruments.py | 0 src/midi_to_song/models.py | 88 ++ src/midi_to_song/timeline/parser.py | 305 +++++++ src/midi_to_song/timeline/processor.py | 264 ++++++ src/midi_to_song/timeline/validation.py | 74 ++ src/utils/__init__.py | 0 10 files changed, 895 insertions(+), 847 deletions(-) create mode 100644 src/arcade/__init__.py delete mode 100644 src/converter/midi_to_song.py create mode 100644 src/midi_to_song/__init__.py rename src/{converter => midi_to_song}/instruments.py (100%) create mode 100644 src/midi_to_song/models.py create mode 100644 src/midi_to_song/timeline/parser.py create mode 100644 src/midi_to_song/timeline/processor.py create mode 100644 src/midi_to_song/timeline/validation.py create mode 100644 src/utils/__init__.py diff --git a/src/arcade/__init__.py b/src/arcade/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/converter/midi_to_song.py b/src/converter/midi_to_song.py deleted file mode 100644 index 3e217ae..0000000 --- a/src/converter/midi_to_song.py +++ /dev/null @@ -1,845 +0,0 @@ -import logging -from collections import defaultdict -from copy import deepcopy -from dataclasses import dataclass -from enum import IntEnum -from math import ceil -from typing import Dict, List, Tuple - -from mido import Message, MidiFile, tick2second - -from arcade.music_types import EnharmonicSpelling, Envelope, Instrument, Note, \ - NoteEvent, Song, Track -from converter.instruments import InstrumentParameterMapping -from utils.logger import create_logger - -logger = create_logger(name=__name__, level=logging.INFO) - - -@dataclass -class AbsoluteTickMessage: - tick: int # absolute MIDI tick - track: int - msg: Message - msg_idx: int - - -@dataclass -class AbsoluteTimeMessage: - time: float # absolute time in seconds - port: int - msg: Message # note_on, note_off, program_change, control_change (where control = 0) - - -def timeline_build(midi_song: MidiFile) -> List[AbsoluteTimeMessage]: - """ - Look through all tracks and convert MIDI's delta tick time to absolute time in - seconds while keeping track of tempo and port changes. - - :param midi_song: `MidiFile` object. - :return: A list of `AbsoluteTimeMessage` objects, which hold time, their original - track, and the MIDI message itself. It is sorted by time in ascending order and - filters out unneeded messages. - """ - # Gather tracks and convert to absolute time - # Unfortunately we can't simply just merge all the tracks because if channel 0 on - # track 0 was a piano and channel 0 on track 1 was a flute, then they collide - # We must keep them separate and build our own global absolute timeline afterward - # Additionally, we must keep track of tempo changes and port changes - - logger.debug("Building global absolute timeline of MIDI messages") - - # First gather all messages with their relative ticks, and convert to absolute ticks - # We need to do this in passes because some messages are globally effective while - # some others are restricted to a track only - logger.debug("Convert relative ticks to absolute ticks and sort") - all_msgs_with_abs_ticks: List[AbsoluteTickMessage] = [] - for i, track in enumerate(midi_song.tracks): - abs_tick = 0 - for j, msg in enumerate(track): - abs_tick += msg.time - all_msgs_with_abs_ticks.append( - AbsoluteTickMessage(tick=abs_tick, track=i, msg=msg, msg_idx=j)) - # Update the sort, time first, then track, then order within the track - all_msgs_with_abs_ticks.sort(key=lambda m: (m.tick, m.track, m.msg_idx)) - - # Now we can convert absolute ticks to absolute time, but we need to keep track of - # tempo changes and MIDI port changes as well (they are also chronological) - logger.debug("Convert absolute ticks to absolute time and track tempo and port " - "changes") - global_timeline: List[AbsoluteTimeMessage] = [] - ticks_per_beat = midi_song.ticks_per_beat - msgs_skipped = 0 - - current_tempo = 500000 # the default - current_abs_time = 0.0 # in seconds (*_time is seconds) - last_abs_ticks = 0 # in MIDI ticks (*_ticks is MIDI ticks) - - track_ports = {i: 0 for i in range(len(midi_song.tracks))} - # Prescan each track for the first meta midi_port message - # Although technically we shouldn't need to do this, some notation software (notably - # MuseScore in my testing) seem to skip midi_port for the first batch of CCs and PC - # in every track, and so CCs and PCs go to port 0 while the note data goes to - # another port - # This sets up a default port that usually works - for i, track in enumerate(midi_song.tracks): - for msg in track: - if msg.type == "midi_port": - track_ports[i] = msg.port - break - - for item in all_msgs_with_abs_ticks: - msg = item.msg - track_idx = item.track - abs_ticks = item.tick - - delta_ticks = abs_ticks - last_abs_ticks - if delta_ticks > 0: - delta_time = tick2second(delta_ticks, ticks_per_beat, current_tempo) - current_abs_time += delta_time - last_abs_ticks = abs_ticks - - if msg.type == "set_tempo": - current_tempo = msg.tempo - elif msg.type == "midi_port": - track_ports[track_idx] = msg.port - # Keep note on/off, program change, sysex, and control change (only if control - # is 0 or 32 which is the bank select MSB/LSB) - elif msg.type in ("note_on", "note_off", "program_change", "sysex") or ( - msg.type == "control_change" and msg.control in (0, 32)): - global_timeline.append( - AbsoluteTimeMessage(time=current_abs_time, port=track_ports[track_idx], - msg=msg)) - else: - msgs_skipped += 1 - logger.debug(f"Global timeline has {len(global_timeline)} messages (skipped " - f"{msgs_skipped}), total song length of {global_timeline[-1].time}s") - - return global_timeline - - -class DrumDeterminationSource(IntEnum): - DEFAULT = 0 - CC = 1 # control change - SYSEX = 2 - - -@dataclass -class ChannelState: - program: int # instrument - bank_select_msb: int - bank_select_lsb: int - is_drum: bool - drum_determined_by: DrumDeterminationSource - - -@dataclass -class AbsoluteTimeMessageWithInstrument: - time: float - port: int - instrument: int # midi instrument - is_drum: bool - msg: Message # note_on and note_off - - -def timeline_find_instrument_data(timeline: List[AbsoluteTimeMessage]) -> List[ - AbsoluteTimeMessageWithInstrument]: - """ - Parse the timeline for program_change and control_change (control = 0) messages to - determine what instrument each message has. - - :param timeline: A list of `AbsoluteTimeMessage` objects. - :return: A list of `AbsoluteTimeMessageWithInstrument` objects. - """ - # Read the timeline sequentially and keep track of program_change and - # control_change (control = 0) to update the current instrument for each channel on - # a port - logger.debug("Finding instrument data for each message in the timeline") - - # Initialize channel states, which keep track of the bank_select and program per - # channel and port - channel_states = {} - highest_port = max([m.port for m in timeline], default=0) - for port in range(highest_port + 1): - for channel in range(16): - # By default, channel 10 starts as drum - channel_states[(port, channel)] = ChannelState(program=0, - bank_select_msb=0, - bank_select_lsb=0, - is_drum=channel == 9, - drum_determined_by=DrumDeterminationSource.DEFAULT) - - # Now search through the timeline and apply control_change (control = 0) and - # program_change messages to the channel states - timeline_with_instrument: List[AbsoluteTimeMessageWithInstrument] = [] - - instr_msgs_processed = 0 - sysex_msgs_processed = 0 - - for item in timeline: - msg = item.msg - port = item.port - channel = getattr(msg, "channel", -1) - - if msg.type == "control_change": - if msg.control == 0: - channel_states[(port, channel)].bank_select_msb = msg.value - elif msg.control == 32: - channel_states[(port, channel)].bank_select_lsb = msg.value - is_drum_bank = ( - channel_states[(port, channel)].bank_select_msb in (120, 121, - 126, - 127) or - channel == 9 - ) - # Only override if last drum determination was weaker than CC - if DrumDeterminationSource.CC >= channel_states[ - (port, channel)].drum_determined_by: - channel_states[(port, channel)].is_drum = is_drum_bank - channel_states[ - (port, channel)].drum_determined_by = DrumDeterminationSource.CC - instr_msgs_processed += 1 - elif msg.type == "program_change": - channel_states[(port, channel)].program = msg.program - instr_msgs_processed += 1 - elif msg.type == "sysex": - data = msg.data - # check for Roland GS - if (len(data) >= 8 and - data[0] == 0x41 and # Roland ID - data[1] == 0x10 and # device ID - data[2] == 0x42 and # GS standard layouts - data[3] == 0x12 and - data[4] == 0x40 and # parameter 1 - 0x10 <= data[5] <= 0x1F and # 0x1n, channel byte, see below - data[6] == 0x15 and # part address - data[7] in (0, 1, 2)): # map byte - # 0x1n is the channel byte, where: - # n=0 is channel 9 (midi channel 10) - # n=1 through 9 is channels 0 through 8 - # n=A through F is channels 10 through 15 - def gs_byte_to_channel(val: int) -> int: - n = val & 0x0F - if n == 0: - return 9 - if n < 10: - return n - 1 - return n - - channel = gs_byte_to_channel(data[5]) - is_drum = data[7] in (1, 2) - # Only override if last drum determination was weaker than sysex - if DrumDeterminationSource.SYSEX >= channel_states[ - (port, channel)].drum_determined_by: - channel_states[(port, channel)].is_drum = is_drum - channel_states[ - (port, - channel)].drum_determined_by = DrumDeterminationSource.SYSEX - # print(f"Roland GS channel {channel} drum: {is_drum}") - instr_msgs_processed += 1 - sysex_msgs_processed += 1 - # check for Yamaha XG - elif (len(data) >= 7 and - data[0] == 0x43 and # Yamaha ID - data[1] == 0x10 and # device ID - data[2] == 0x4C and # XG model ID - data[3] == 0x08 and # multi part params - 0x00 <= data[4] <= 0x0F and # channel, direct mapping - data[5] == 0x07 and # part address - data[6] >= 0): # map byte, technically redundant but - channel = data[4] - is_drum = data[6] > 0 - # Only override if last drum determination was weaker than sysex - if DrumDeterminationSource.SYSEX >= channel_states[ - (port, channel)].drum_determined_by: - channel_states[(port, channel)].is_drum = is_drum - channel_states[ - (port, - channel)].drum_determined_by = DrumDeterminationSource.SYSEX - # print(f"Yamaha XG channel {channel} drum: {is_drum}") - instr_msgs_processed += 1 - sysex_msgs_processed += 1 - elif msg.type in ("note_on", "note_off"): - timeline_with_instrument.append(AbsoluteTimeMessageWithInstrument( - time=item.time, - port=item.port, - instrument=channel_states[(port, channel)].program, - is_drum=channel_states[(port, channel)].is_drum, - msg=msg - )) - logger.debug(f"Global timeline has {len(timeline_with_instrument)} note messages (" - f"processed {instr_msgs_processed} instrument messages, " - f"{sysex_msgs_processed} of which were recognized SysEx messages)") - - return timeline_with_instrument - - -@dataclass -class AbsoluteCompleteNote: - start_time: float - end_time: float - - note: int - velocity: int - - instrument: int - is_drum: bool - - -def timeline_group_messages(timeline: List[AbsoluteTimeMessageWithInstrument]) -> List[ - AbsoluteCompleteNote]: - """ - Parse the timeline for note_on and note_off messages to determine the start and end - times of each note. - - :param timeline: A list of `AbsoluteTimeMessageWithInstrument` objects. - :return: A list of `AbsoluteCompleteNote` objects. - """ - # Find all note_on and note_on (velocity = 0) and note_off messages, and pair them - # up - logger.debug("Grouping messages into complete notes in the timeline") - - timeline_with_complete_notes = [] - highest_port = max([0] + [m.port for m in timeline]) - active_notes: Dict[Tuple[int, int], List[AbsoluteCompleteNote]] = { - (port, channel): [] - for port in range(highest_port + 1) - for channel in range(16) - } - last_time = 0 - - current_poly = 0 - max_poly = 0 - - for item in timeline: - msg = item.msg - port_and_channel = (item.port, msg.channel) - last_time = max(last_time, item.time) - - if msg.type == "note_on" and msg.velocity > 0: - # add to the list of playing notes - active_notes[port_and_channel].append( - AbsoluteCompleteNote( - start_time=item.time, - end_time=item.time, # will be updated when note_off found - note=msg.note, - velocity=msg.velocity, - instrument=item.instrument, - is_drum=item.is_drum, - ) - ) - current_poly += 1 - elif msg.type == "note_off" or (msg.type == "note_on" and msg.velocity == 0): - # find the playing note and finish it - for playing_note in active_notes[port_and_channel]: - if playing_note.note == msg.note: - playing_note.end_time = item.time - timeline_with_complete_notes.append(playing_note) - active_notes[port_and_channel].remove(playing_note) - current_poly -= 1 - break - else: - logger.warning(f"Could not find start of note for {item}") - max_poly = max(max_poly, current_poly) - - # handle hanging notes - hanging_count = 0 - for group_key in active_notes: - for playing_note in active_notes[group_key]: - playing_note.end_time = last_time - timeline_with_complete_notes.append(playing_note) - hanging_count += 1 - # no need to remove we're cleaning up - - # sort by start instead of when they ended - timeline_with_complete_notes.sort(key=lambda m: m.start_time) - - logger.debug(f"Global timeline has {len(timeline_with_complete_notes)} note events " - f"(maximum polyphony across all channels and ports was {max_poly} " - f"notes and had to clean up {hanging_count} hanging notes)") - - return timeline_with_complete_notes - - -@dataclass -class AbsoluteCompleteNoteWithTick: - start_tick: int - end_tick: int - - note: int - velocity: int - - instrument: int - is_drum: bool - - -def timeline_quantize_to_song_ticks(timeline: List[AbsoluteCompleteNote], - song: Song) -> List[AbsoluteCompleteNoteWithTick]: - """ - Given the song's BPM and TPB, quantize the timeline's start and end times to ticks. - - :param timeline: A list of `AbsoluteCompleteNote` objects. - :param song: The `Song` object to use. - :return: A list of `AbsoluteCompleteNoteWithTick` objects. - """ - tick_time = (60 / song.beats_per_minute) / song.ticks_per_beat # in secs - logger.debug(f"Quantizing note times to ticks based of song BPM of " - f"{song.beats_per_minute} and TPB of {song.ticks_per_beat} - one tick " - f"is 1/{1 / tick_time} ({tick_time}) seconds long") - - res = [] - - for old_note in timeline: - new_start_tick = round(old_note.start_time / tick_time) - # Ensure all notes last for one tick - new_end_tick = max(round(old_note.end_time / tick_time), new_start_tick + 1) - res.append(AbsoluteCompleteNoteWithTick( - start_tick=new_start_tick, - end_tick=new_end_tick, - note=old_note.note, - velocity=old_note.velocity, - instrument=old_note.instrument, - is_drum=old_note.is_drum - )) - - return res - - -def find_all_melodic_instruments(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[ - int]: - """ - Search the timeline for all unique melodic instruments. - - :param timeline: A list of `AbsoluteTimeMessage` objects. - :return: A list of ints, representing what general MIDI instruments are in the song. - """ - logger.debug("Finding all melodic instruments in the timeline") - - return list(sorted(set([m.instrument for m in timeline if not m.is_drum]))) - - -def find_all_drum_notes_used(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[int]: - """ - Search the timeline for all unique drum notes. - - :param timeline: A list of `AbsoluteTimeMessage` objects. - :return: A list of ints, representing what general MIDI drum notes are in the song. - """ - logger.debug("Finding all drum notes in the timeline") - - return list(sorted(set([m.note for m in timeline if m.is_drum]))) - - -def find_all_drum_chords_used(timeline: List[AbsoluteCompleteChordWithTick]) -> List[ - int]: - """ - Search the timeline for all unique drum notes, for a list of chords. - - :param timeline: A list of `AbsoluteCompleteChordWithTick` objects. - :return: A list of ints, representing what general MIDI drum notes are in the song. - """ - logger.debug(f"Finding all drum chords in the timeline") - - return list( - sorted({note for chord in timeline if chord.is_drum for note in chord.notes})) - - -def timeline_group_by_instrument(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[ - List[AbsoluteCompleteNoteWithTick]]: - """ - Split up the timeline by instrument. - - :param timeline: A list of `AbsoluteCompleteNote` objects. - :return: A list of lists of `AbsoluteCompleteNoteWithTick` objects. (Each list of - notes within the list have the same instrument) - """ - logger.debug("Splitting up the timeline by instruments") - - used_melodics = find_all_melodic_instruments(timeline) - used_drums = find_all_drum_notes_used(timeline) - logger.debug(f"Song used {len(used_melodics)} melodic instruments and " - f"{len(used_drums)} unique drum notes") - - tracks = [] - - for melodic in used_melodics: - tracks.append([note for note in timeline if - note.instrument == melodic and not note.is_drum]) - - if len(used_drums) > 0: - tracks.append([note for note in timeline if note.is_drum]) - - logger.debug(f"Split up global timeline into {len(tracks)} tracks") - - return tracks - - -def timeline_split_into_two_tracks_if_needed( - timeline: List[List[AbsoluteCompleteNoteWithTick]]) -> List[ - List[AbsoluteCompleteNoteWithTick]]: - """ - Go through the melodic tracks in the timeline and check the highest and lowest note - in each track. If it can't fit into one track (which has a range limit of 64 notes - from an octave offset) then we use two tracks and move notes as necessary. - - :param timeline: A list of lists of `AbsoluteCompleteNote` objects. - :return: A list of lists of `AbsoluteCompleteNoteWithTick` objects. - """ - logger.debug("Checking necessity to split track into two tracks for range") - - new_tracks: List[List[AbsoluteCompleteNoteWithTick]] = [] - - tracks_that_fit = 0 - tracks_that_split = 0 - - for old_track in timeline: - # drum tracks don't use octave offsets, only 61 samples max as well - if old_track[0].is_drum: - new_tracks.append(old_track) - tracks_that_fit += 1 - continue - - all_notes = [n.note for n in old_track] - highest_note = max(all_notes) - lowest_note = min(all_notes) - - def octave_offset_work(octave: int) -> bool: - return ((((octave - 2) * 12) <= lowest_note) and - (highest_note <= ((octave - 2) * 12 + 63))) - - # does ANY octave offset from [0, 9] work? - if any([octave_offset_work(o) for o in range(0, 10)]): - # we don't need to modify, when constructing the MakeCode Arcade Tracks, - # we'll find the correct octave offset again - new_tracks.append(old_track) - tracks_that_fit += 1 - else: - # split into two tracks, using octave offsets 0 and 6 guarantee covering the - # full shifted MIDI range - low_track = [n for n in old_track if n.note < 41] - high_track = [n for n in old_track if n.note >= 41] - new_tracks.append(low_track) - new_tracks.append(high_track) - tracks_that_split += 1 - - logger.debug(f"{tracks_that_fit} tracks fit within one track's range, " - f"{tracks_that_split} tracks had to split, total of {len(new_tracks)} " - f"tracks in timeline") - - return new_tracks - - -@dataclass -class AbsoluteCompleteChordWithTick: - start_tick: int - end_tick: int - - notes: List[int] - velocity: int - - instrument: int - is_drum: bool - - -def timeline_group_into_perfect_chords( - timeline: List[List[AbsoluteCompleteNoteWithTick]]) -> List[ - List[AbsoluteCompleteChordWithTick]]: - """ - Go through the tracks in the timeline and group up notes that share the same start - and end tick and velocity and instruments to create "perfect" chords. - - :param timeline: A list of lists of `AbsoluteCompleteNoteWithTick` objects. - :return: A list of lists of `AbsoluteCompleteChordWithTick` objects. - """ - logger.debug("Grouping notes in timeline into perfect chords") - - new_tracks: List[List[AbsoluteCompleteChordWithTick]] = [] - - old_note_count = sum(len(track) for track in timeline) - - for old_track in timeline: - # use dictionaries to quickly find existing chords - # key is a tuple of (start_tick, end_tick, velocity, instrument, is_drum) - # values are notes in the chord - chords: Dict[Tuple[int, int, int, int, bool], List[int]] = defaultdict(list) - - for note in old_track: - key = (note.start_tick, note.end_tick, note.velocity, note.instrument, - note.is_drum) - # if a note has the same start and end tick and velocity and instrument - # they can be played as a chord - chords[key].append(note.note) - - new_track: List[AbsoluteCompleteChordWithTick] = [] - for (start_tick, end_tick, velocity, instrument, - is_drum), notes in chords.items(): - new_track.append(AbsoluteCompleteChordWithTick( - start_tick=start_tick, end_tick=end_tick, - notes=notes, - velocity=velocity, - instrument=instrument, is_drum=is_drum - )) - new_track.sort(key=lambda c: (c.start_tick, c.end_tick)) - new_tracks.append(new_track) - - new_chord_count = sum(len(track) for track in new_tracks) - - logger.debug(f"Created " - f"{sum([sum([1 if len(n.notes) > 1 else 0 for n in t]) for t in new_tracks])}" - f" multi-note chords (dropped from {old_note_count} notes to " - f"{new_chord_count} chords)") - - return new_tracks - - -def timeline_resolve_overlapping_chords( - timeline: List[List[AbsoluteCompleteChordWithTick]]) -> List[ - List[AbsoluteCompleteChordWithTick]]: - """ - Go through the melodic tracks in the timeline and check for chords overlapping in a - track. If they are, move them to an extra track. If there are no free tracks, create - one. This minimizes the number of extra tracks required while keeping the desired - polyphony of MIDI. - - :param timeline: A list of lists of `AbsoluteCompleteChordWithTick` objects. - :return: A list of lists of `AbsoluteCompleteChordWithTick` objects. - """ - logger.debug("Resolving overlapping chords") - - new_tracks: List[List[AbsoluteCompleteChordWithTick]] = [] - old_track_count = len(timeline) - - for old_track in timeline: - new_sub_tracks: List[List[AbsoluteCompleteChordWithTick]] = [[]] - - # don't have to worry about instrument matching because chords in old_track - # should all have the same instrument - for chord in old_track: - # try to place into a sub track - for sub_track in new_sub_tracks: - # if the last note in the subtrack has ended (or it's empty) - # TODO: Test if this needs to be < or <= works - # theoretically it should work fine with <= (and this will save tracks) - # but < will guarantee a "rest" - if len(sub_track) == 0 or sub_track[-1].end_tick < chord.start_tick: - sub_track.append(chord) - break - else: - # no free sub tracks, create - new_sub_tracks.append([chord]) - - # dump all generated sub tracks directly into the new timeline - new_tracks.extend(new_sub_tracks) - - new_track_count = len(new_tracks) - logger.debug(f"Created {new_track_count - old_track_count} extra tracks to handle " - f"overlapping chords (from {old_track_count} to {new_track_count} " - f"tracks)") - - return new_tracks - - -def timeline_checks(timeline: List[List[AbsoluteCompleteChordWithTick]]): - """ - Run some basic checks on the timeline to verify assumptions before mapping to the - MakeCode Arcade dataclasses. - - :param timeline: A list of lists of `AbsoluteCompleteChordWithTick` objects. - :raises ValueError: If any violations are detected. - """ - logger.debug("Running checks on the timeline") - # Track count must be less than 256 - if len(timeline) > 255: - raise ValueError(f"Too many tracks in the timeline! (max of 255, found " - f"{len(timeline)}) Reduce polyphony or unique instruments.") - for track in timeline: - if len(track) == 0: - raise Warning("Empty track in the timeline!") - # Sort by start tick as required, just in case - track.sort(key=lambda c: c.start_tick) - # The start and end ticks must be less than 65536 - highest_tick = max( - [chord.start_tick for chord in track] + [chord.end_tick for chord in track]) - if highest_tick > 65535: - raise ValueError(f"Chord start or end tick is too high! (max of 65535, " - f"found {highest_tick}) Decrease song length or reduce " - f"BPM/TPB at the cost of worse timing.") - # The highest and lowest notes must fit within 64 notes of an integer octave - # offset for melodic instruments - if not track[0].is_drum: - highest_note = max([max(chord.notes) for chord in track]) - lowest_note = min([min(chord.notes) for chord in track]) - - def octave_offset_work(octave: int) -> bool: - return ((((octave - 2) * 12) <= lowest_note) and - (highest_note <= ((octave - 2) * 12 + 63))) - - if not any([octave_offset_work(o) for o in range(0, 10)]): - raise ValueError(f"Track range too big to fit! (please report)") - # Chord must have less than 256 notes - max_chord_polyphony = max([len(chord.notes) for chord in track]) - if max_chord_polyphony > 255: - raise ValueError(f"Chord has too many notes! (max of 255, found " - f"{max_chord_polyphony})") - # Chords must last at least 1 tick - min_chord_duration = min([chord.end_tick - chord.start_tick for chord in track]) - if min_chord_duration < 1: - raise ValueError(f"Chord violated minimum duration time! (min of 1 tick, " - f"found {min_chord_duration} ticks, please report)") - # All chords must have the same instrument if melodic or all chords must be - # marked as drums in a drum track - if track[0].is_drum: - if any([not chord.is_drum for chord in track]): - raise ValueError(f"Found non drum chord in drum track! (please report") - else: - instruments = set([chord.instrument for chord in track]) - if len(instruments) > 1: - raise ValueError(f"Track has more than one instrument! (found " - f"{len(instruments)}, please report)") - # No chords overlap, i.e. start_tick >= end_tick of prev chord - for i in range(1, len(track)): - prev_chord = track[i - 1] - this_chord = track[i] - if prev_chord.end_tick > this_chord.start_tick: - raise ValueError(f"Chord overlapped! (please report)") - - logger.debug("Timeline passes all checks") - - -def convert_midi_to_song(midi_song: MidiFile, - mapping: InstrumentParameterMapping) -> Song: - """ - Convert a MIDI file into a MakeCode Arcade song. - - :param midi_song: A `MidiFile` object. - :param mapping: An `InstrumentParameterMapping` object, loaded from - `load_instrument_params`. - :return: MakeCode Arcade `Song` object. - """ - logger.debug("Converting MIDI file into MakeCode Arcade song") - - song = Song( - measures=1, - beats_per_measure=4, - beats_per_minute=120, # beat every 1/2 seconds - ticks_per_beat=24, # each tick is 1/48 seconds long - tracks=[] - ) - - logger.debug("Resolving timeline") - - global_timeline: List[AbsoluteTimeMessage] = timeline_build(midi_song) - global_timeline: List[ - AbsoluteTimeMessageWithInstrument] = timeline_find_instrument_data( - global_timeline) - global_timeline: List[AbsoluteCompleteNote] = timeline_group_messages( - global_timeline) - - # MIDI file with C4 (MIDI 60) plays at B5 (MIDI 83) - # This is because MakeCode Arcade defines C4 as 49 instead of 60 - # And now I have no idea why I need to shift down another octave but then it works - # Drums don't need this because we already map from MIDI drum notes to an index into - # a list of drum instruments in a track, which we control - for note in global_timeline: - if not note.is_drum: - note.note -= 11 # MIDI 60 (C4) maps to Arcade's C4 of 49 - note.note -= 12 # another octave down makes it correct - - global_timeline: List[ - AbsoluteCompleteNoteWithTick] = timeline_quantize_to_song_ticks(global_timeline, - song) - global_timeline: List[ - List[AbsoluteCompleteNoteWithTick]] = timeline_group_by_instrument( - global_timeline) - global_timeline = timeline_split_into_two_tracks_if_needed(global_timeline) - global_timeline: List[ - List[AbsoluteCompleteChordWithTick]] = timeline_group_into_perfect_chords( - global_timeline) - global_timeline: List[ - List[AbsoluteCompleteChordWithTick]] = timeline_resolve_overlapping_chords( - global_timeline) - - # Raises exceptions on check failures - timeline_checks(global_timeline) - - # With all this pitch checks and timing manipulations done to fit MakeCode Arcade's - # song's constraints, we should be able to basically map 1-1 to the MakeCode Arcade - # dataclasses - logger.debug("Timeline resolved, mapping to MakeCode Arcade song") - - next_id = 0 - highest_tick = 0 - - for old_track in global_timeline: - this_track_is_drum = old_track[0].is_drum - highest_tick = max([highest_tick] + [c.end_tick for c in old_track]) - - # for drums - midi_drum_to_drum_idx: Dict[int, int] = {} - if this_track_is_drum: - # shouldn't matter, copied from get_empty_song to satisfy types and song - # packing - instrument = Instrument( - waveform=11, - octave=4, - amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=100, - amplitude=1024) - ) - # actually load the drums in - # and keep what midi note to what sample index they should go to - drums = [] - used_drum_notes = find_all_drum_chords_used(old_track) - for i, drum_note in enumerate(used_drum_notes): - drums.append(mapping.drum_instruments[drum_note]) - midi_drum_to_drum_idx[drum_note] = i - else: - instrument = deepcopy(mapping.melodic_instruments[old_track[0].instrument]) - # determine the optimal octave offset - highest_note = max([max(chord.notes) for chord in old_track]) - lowest_note = min([min(chord.notes) for chord in old_track]) - - def octave_offset_work(octave: int) -> bool: - return ((((octave - 2) * 12) <= lowest_note) and - (highest_note <= ((octave - 2) * 12 + 63))) - - for potential_offset in range(0, 10): # find the first offset that works - if octave_offset_work(potential_offset): - instrument.octave = potential_offset - break - else: - raise ValueError(f"Track range too big to fit! (please report)") - # none for melodic instrument - drums = None - new_track = Track( - id=next_id, - instrument=instrument, - drums=drums, - notes=[], - ) - for chord in old_track: - if this_track_is_drum: - notes = [midi_drum_to_drum_idx[note] for note in - chord.notes] - else: - notes = chord.notes - new_track.notes.append(NoteEvent( - notes=[Note(note=n, enharmonic_spelling=EnharmonicSpelling.NORMAL) for n - in notes], - start_tick=chord.start_tick, - end_tick=chord.end_tick, - velocity=chord.velocity - )) - - song.tracks.append(new_track) - next_id += 1 - - # fix the ending measure count - ticks_per_measure = song.beats_per_measure * song.ticks_per_beat - song.measures = ceil(highest_tick / ticks_per_measure) - - time_for_tick = (60 / song.beats_per_minute) / song.ticks_per_beat - logger.debug(f"Finished mapping to MakeCode Arcade song with {len(song.tracks)} " - f"tracks, length of {highest_tick} ticks which is " - f"{highest_tick * time_for_tick} seconds") - - return song diff --git a/src/main.py b/src/main.py index ac5b073..af28641 100644 --- a/src/main.py +++ b/src/main.py @@ -5,8 +5,8 @@ from mido import MidiFile from arcade.music import encode_song_to_hex -from converter.instruments import load_instrument_params -from converter.midi_to_song import convert_midi_to_song +from midi_to_song import convert_midi_to_song +from midi_to_song.instruments import load_instrument_params from utils.logger import create_logger, set_all_stdout_logger_levels parser = ArgumentParser(description="Convert a MIDI file to a MakeCode Arcade song.") diff --git a/src/midi_to_song/__init__.py b/src/midi_to_song/__init__.py new file mode 100644 index 0000000..c81b477 --- /dev/null +++ b/src/midi_to_song/__init__.py @@ -0,0 +1,162 @@ +import logging +from copy import deepcopy +from math import ceil +from typing import Dict, List + +from mido import MidiFile + +from arcade.music_types import EnharmonicSpelling, Envelope, Instrument, Note, \ + NoteEvent, Song, Track +from midi_to_song.instruments import InstrumentParameterMapping +from midi_to_song.models import AbsoluteCompleteChordWithTick, AbsoluteCompleteNote, \ + AbsoluteCompleteNoteWithTick, AbsoluteTickMessage, AbsoluteTimeMessage, \ + AbsoluteTimeMessageWithInstrument, ChannelState, DrumDeterminationSource +from midi_to_song.timeline.parser import timeline_build, timeline_find_instrument_data, \ + timeline_group_messages +from midi_to_song.timeline.processor import find_all_drum_chords_used, \ + timeline_group_by_instrument, timeline_group_into_perfect_chords, \ + timeline_quantize_to_song_ticks, timeline_resolve_overlapping_chords, \ + timeline_split_into_two_tracks_if_needed +from midi_to_song.timeline.validation import timeline_checks +from utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +def convert_midi_to_song(midi_song: MidiFile, + mapping: InstrumentParameterMapping) -> Song: + """ + Convert a MIDI file into a MakeCode Arcade song. + + :param midi_song: A `MidiFile` object. + :param mapping: An `InstrumentParameterMapping` object, loaded from + `load_instrument_params`. + :return: MakeCode Arcade `Song` object. + """ + logger.debug("Converting MIDI file into MakeCode Arcade song") + + song = Song( + measures=1, + beats_per_measure=4, + beats_per_minute=120, # beat every 1/2 seconds + ticks_per_beat=24, # each tick is 1/48 seconds long + tracks=[] + ) + + logger.debug("Resolving timeline") + + global_timeline: List[AbsoluteTimeMessage] = timeline_build(midi_song) + global_timeline: List[ + AbsoluteTimeMessageWithInstrument] = timeline_find_instrument_data( + global_timeline) + global_timeline: List[AbsoluteCompleteNote] = timeline_group_messages( + global_timeline) + + # MIDI file with C4 (MIDI 60) plays at B5 (MIDI 83) + # This is because MakeCode Arcade defines C4 as 49 instead of 60 + # And now I have no idea why I need to shift down another octave but then it works + # Drums don't need this because we already map from MIDI drum notes to an index into + # a list of drum instruments in a track, which we control + for note in global_timeline: + if not note.is_drum: + note.note -= 11 # MIDI 60 (C4) maps to Arcade's C4 of 49 + note.note -= 12 # another octave down makes it correct + + global_timeline: List[ + AbsoluteCompleteNoteWithTick] = timeline_quantize_to_song_ticks(global_timeline, + song) + global_timeline: List[ + List[AbsoluteCompleteNoteWithTick]] = timeline_group_by_instrument( + global_timeline) + global_timeline = timeline_split_into_two_tracks_if_needed(global_timeline) + global_timeline: List[ + List[AbsoluteCompleteChordWithTick]] = timeline_group_into_perfect_chords( + global_timeline) + global_timeline: List[ + List[AbsoluteCompleteChordWithTick]] = timeline_resolve_overlapping_chords( + global_timeline) + + # Raises exceptions on check failures + timeline_checks(global_timeline) + + # With all this pitch checks and timing manipulations done to fit MakeCode Arcade's + # song's constraints, we should be able to basically map 1-1 to the MakeCode Arcade + # dataclasses + logger.debug("Timeline resolved, mapping to MakeCode Arcade song") + + next_id = 0 + highest_tick = 0 + + for old_track in global_timeline: + this_track_is_drum = old_track[0].is_drum + highest_tick = max([highest_tick] + [c.end_tick for c in old_track]) + + # for drums + midi_drum_to_drum_idx: Dict[int, int] = {} + if this_track_is_drum: + # shouldn't matter, copied from get_empty_song to satisfy types and song + # packing + instrument = Instrument( + waveform=11, + octave=4, + amp_envelope=Envelope(attack=10, decay=100, sustain=500, release=100, + amplitude=1024) + ) + # actually load the drums in + # and keep what midi note to what sample index they should go to + drums = [] + used_drum_notes = find_all_drum_chords_used(old_track) + for i, drum_note in enumerate(used_drum_notes): + drums.append(mapping.drum_instruments[drum_note]) + midi_drum_to_drum_idx[drum_note] = i + else: + instrument = deepcopy(mapping.melodic_instruments[old_track[0].instrument]) + # determine the optimal octave offset + highest_note = max([max(chord.notes) for chord in old_track]) + lowest_note = min([min(chord.notes) for chord in old_track]) + + def octave_offset_work(octave: int) -> bool: + return ((((octave - 2) * 12) <= lowest_note) and + (highest_note <= ((octave - 2) * 12 + 63))) + + for potential_offset in range(0, 10): # find the first offset that works + if octave_offset_work(potential_offset): + instrument.octave = potential_offset + break + else: + raise ValueError(f"Track range too big to fit! (please report)") + # none for melodic instrument + drums = None + new_track = Track( + id=next_id, + instrument=instrument, + drums=drums, + notes=[], + ) + for chord in old_track: + if this_track_is_drum: + notes = [midi_drum_to_drum_idx[note] for note in + chord.notes] + else: + notes = chord.notes + new_track.notes.append(NoteEvent( + notes=[Note(note=n, enharmonic_spelling=EnharmonicSpelling.NORMAL) for n + in notes], + start_tick=chord.start_tick, + end_tick=chord.end_tick, + velocity=chord.velocity + )) + + song.tracks.append(new_track) + next_id += 1 + + # fix the ending measure count + ticks_per_measure = song.beats_per_measure * song.ticks_per_beat + song.measures = ceil(highest_tick / ticks_per_measure) + + time_for_tick = (60 / song.beats_per_minute) / song.ticks_per_beat + logger.debug(f"Finished mapping to MakeCode Arcade song with {len(song.tracks)} " + f"tracks, length of {highest_tick} ticks which is " + f"{highest_tick * time_for_tick} seconds") + + return song diff --git a/src/converter/instruments.py b/src/midi_to_song/instruments.py similarity index 100% rename from src/converter/instruments.py rename to src/midi_to_song/instruments.py diff --git a/src/midi_to_song/models.py b/src/midi_to_song/models.py new file mode 100644 index 0000000..84e9fbd --- /dev/null +++ b/src/midi_to_song/models.py @@ -0,0 +1,88 @@ +import logging +from dataclasses import dataclass +from enum import IntEnum +from typing import List + +from mido import Message + +from utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +# All intermediate representations used during conversion + + +@dataclass +class AbsoluteTickMessage: + tick: int # absolute MIDI tick + track: int + msg: Message + msg_idx: int + + +@dataclass +class AbsoluteTimeMessage: + time: float # absolute time in seconds + port: int + msg: Message # note_on, note_off, program_change, control_change (where control = 0) + + +@dataclass +class ChannelState: + program: int # instrument + bank_select_msb: int + bank_select_lsb: int + is_drum: bool + drum_determined_by: DrumDeterminationSource + + +class DrumDeterminationSource(IntEnum): + DEFAULT = 0 + CC = 1 # control change + SYSEX = 2 + + +@dataclass +class AbsoluteTimeMessageWithInstrument: + time: float + port: int + instrument: int # midi instrument + is_drum: bool + msg: Message # note_on and note_off + + +@dataclass +class AbsoluteCompleteNote: + start_time: float + end_time: float + + note: int + velocity: int + + instrument: int + is_drum: bool + + +@dataclass +class AbsoluteCompleteNoteWithTick: + start_tick: int + end_tick: int + + note: int + velocity: int + + instrument: int + is_drum: bool + + +@dataclass +class AbsoluteCompleteChordWithTick: + start_tick: int + end_tick: int + + notes: List[int] + velocity: int + + instrument: int + is_drum: bool diff --git a/src/midi_to_song/timeline/parser.py b/src/midi_to_song/timeline/parser.py new file mode 100644 index 0000000..11ad2c2 --- /dev/null +++ b/src/midi_to_song/timeline/parser.py @@ -0,0 +1,305 @@ +import logging +from typing import Dict, List, Tuple + +from mido import MidiFile, tick2second + +from midi_to_song.models import AbsoluteCompleteNote, \ + AbsoluteTickMessage, AbsoluteTimeMessage, \ + AbsoluteTimeMessageWithInstrument, ChannelState, DrumDeterminationSource +from utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +def timeline_build(midi_song: MidiFile) -> List[AbsoluteTimeMessage]: + """ + Look through all tracks and convert MIDI's delta tick time to absolute time in + seconds while keeping track of tempo and port changes. + + :param midi_song: `MidiFile` object. + :return: A list of `AbsoluteTimeMessage` objects, which hold time, their original + track, and the MIDI message itself. It is sorted by time in ascending order and + filters out unneeded messages. + """ + # Gather tracks and convert to absolute time + # Unfortunately we can't simply just merge all the tracks because if channel 0 on + # track 0 was a piano and channel 0 on track 1 was a flute, then they collide + # We must keep them separate and build our own global absolute timeline afterward + # Additionally, we must keep track of tempo changes and port changes + + logger.debug("Building global absolute timeline of MIDI messages") + + # First gather all messages with their relative ticks, and convert to absolute ticks + # We need to do this in passes because some messages are globally effective while + # some others are restricted to a track only + logger.debug("Convert relative ticks to absolute ticks and sort") + all_msgs_with_abs_ticks: List[AbsoluteTickMessage] = [] + for i, track in enumerate(midi_song.tracks): + abs_tick = 0 + for j, msg in enumerate(track): + abs_tick += msg.time + all_msgs_with_abs_ticks.append( + AbsoluteTickMessage(tick=abs_tick, track=i, msg=msg, msg_idx=j)) + # Update the sort, time first, then track, then order within the track + all_msgs_with_abs_ticks.sort(key=lambda m: (m.tick, m.track, m.msg_idx)) + + # Now we can convert absolute ticks to absolute time, but we need to keep track of + # tempo changes and MIDI port changes as well (they are also chronological) + logger.debug("Convert absolute ticks to absolute time and track tempo and port " + "changes") + global_timeline: List[AbsoluteTimeMessage] = [] + ticks_per_beat = midi_song.ticks_per_beat + msgs_skipped = 0 + + current_tempo = 500000 # the default + current_abs_time = 0.0 # in seconds (*_time is seconds) + last_abs_ticks = 0 # in MIDI ticks (*_ticks is MIDI ticks) + + track_ports = {i: 0 for i in range(len(midi_song.tracks))} + # Prescan each track for the first meta midi_port message + # Although technically we shouldn't need to do this, some notation software (notably + # MuseScore in my testing) seem to skip midi_port for the first batch of CCs and PC + # in every track, and so CCs and PCs go to port 0 while the note data goes to + # another port + # This sets up a default port that usually works + for i, track in enumerate(midi_song.tracks): + for msg in track: + if msg.type == "midi_port": + track_ports[i] = msg.port + break + + for item in all_msgs_with_abs_ticks: + msg = item.msg + track_idx = item.track + abs_ticks = item.tick + + delta_ticks = abs_ticks - last_abs_ticks + if delta_ticks > 0: + delta_time = tick2second(delta_ticks, ticks_per_beat, current_tempo) + current_abs_time += delta_time + last_abs_ticks = abs_ticks + + if msg.type == "set_tempo": + current_tempo = msg.tempo + elif msg.type == "midi_port": + track_ports[track_idx] = msg.port + # Keep note on/off, program change, sysex, and control change (only if control + # is 0 or 32 which is the bank select MSB/LSB) + elif msg.type in ("note_on", "note_off", "program_change", "sysex") or ( + msg.type == "control_change" and msg.control in (0, 32)): + global_timeline.append( + AbsoluteTimeMessage(time=current_abs_time, port=track_ports[track_idx], + msg=msg)) + else: + msgs_skipped += 1 + logger.debug(f"Global timeline has {len(global_timeline)} messages (skipped " + f"{msgs_skipped}), total song length of {global_timeline[-1].time}s") + + return global_timeline + + +def timeline_find_instrument_data(timeline: List[AbsoluteTimeMessage]) -> List[ + AbsoluteTimeMessageWithInstrument]: + """ + Parse the timeline for program_change and control_change (control = 0) messages to + determine what instrument each message has. + + :param timeline: A list of `AbsoluteTimeMessage` objects. + :return: A list of `AbsoluteTimeMessageWithInstrument` objects. + """ + # Read the timeline sequentially and keep track of program_change and + # control_change (control = 0) to update the current instrument for each channel on + # a port + logger.debug("Finding instrument data for each message in the timeline") + + # Initialize channel states, which keep track of the bank_select and program per + # channel and port + channel_states = {} + highest_port = max([m.port for m in timeline], default=0) + for port in range(highest_port + 1): + for channel in range(16): + # By default, channel 10 starts as drum + channel_states[(port, channel)] = ChannelState(program=0, + bank_select_msb=0, + bank_select_lsb=0, + is_drum=channel == 9, + drum_determined_by=DrumDeterminationSource.DEFAULT) + + # Now search through the timeline and apply control_change (control = 0) and + # program_change messages to the channel states + timeline_with_instrument: List[AbsoluteTimeMessageWithInstrument] = [] + + instr_msgs_processed = 0 + sysex_msgs_processed = 0 + + for item in timeline: + msg = item.msg + port = item.port + channel = getattr(msg, "channel", -1) + + if msg.type == "control_change": + if msg.control == 0: + channel_states[(port, channel)].bank_select_msb = msg.value + elif msg.control == 32: + channel_states[(port, channel)].bank_select_lsb = msg.value + is_drum_bank = ( + channel_states[(port, channel)].bank_select_msb in (120, 121, + 126, + 127) or + channel == 9 + ) + # Only override if last drum determination was weaker than CC + if DrumDeterminationSource.CC >= channel_states[ + (port, channel)].drum_determined_by: + channel_states[(port, channel)].is_drum = is_drum_bank + channel_states[ + (port, channel)].drum_determined_by = DrumDeterminationSource.CC + instr_msgs_processed += 1 + elif msg.type == "program_change": + channel_states[(port, channel)].program = msg.program + instr_msgs_processed += 1 + elif msg.type == "sysex": + data = msg.data + # check for Roland GS + if (len(data) >= 8 and + data[0] == 0x41 and # Roland ID + data[1] == 0x10 and # device ID + data[2] == 0x42 and # GS standard layouts + data[3] == 0x12 and + data[4] == 0x40 and # parameter 1 + 0x10 <= data[5] <= 0x1F and # 0x1n, channel byte, see below + data[6] == 0x15 and # part address + data[7] in (0, 1, 2)): # map byte + # 0x1n is the channel byte, where: + # n=0 is channel 9 (midi channel 10) + # n=1 through 9 is channels 0 through 8 + # n=A through F is channels 10 through 15 + def gs_byte_to_channel(val: int) -> int: + n = val & 0x0F + if n == 0: + return 9 + if n < 10: + return n - 1 + return n + + channel = gs_byte_to_channel(data[5]) + is_drum = data[7] in (1, 2) + # Only override if last drum determination was weaker than sysex + if DrumDeterminationSource.SYSEX >= channel_states[ + (port, channel)].drum_determined_by: + channel_states[(port, channel)].is_drum = is_drum + channel_states[ + (port, + channel)].drum_determined_by = DrumDeterminationSource.SYSEX + # print(f"Roland GS channel {channel} drum: {is_drum}") + instr_msgs_processed += 1 + sysex_msgs_processed += 1 + # check for Yamaha XG + elif (len(data) >= 7 and + data[0] == 0x43 and # Yamaha ID + data[1] == 0x10 and # device ID + data[2] == 0x4C and # XG model ID + data[3] == 0x08 and # multi part params + 0x00 <= data[4] <= 0x0F and # channel, direct mapping + data[5] == 0x07 and # part address + data[6] >= 0): # map byte, technically redundant but + channel = data[4] + is_drum = data[6] > 0 + # Only override if last drum determination was weaker than sysex + if DrumDeterminationSource.SYSEX >= channel_states[ + (port, channel)].drum_determined_by: + channel_states[(port, channel)].is_drum = is_drum + channel_states[ + (port, + channel)].drum_determined_by = DrumDeterminationSource.SYSEX + # print(f"Yamaha XG channel {channel} drum: {is_drum}") + instr_msgs_processed += 1 + sysex_msgs_processed += 1 + elif msg.type in ("note_on", "note_off"): + timeline_with_instrument.append(AbsoluteTimeMessageWithInstrument( + time=item.time, + port=item.port, + instrument=channel_states[(port, channel)].program, + is_drum=channel_states[(port, channel)].is_drum, + msg=msg + )) + logger.debug(f"Global timeline has {len(timeline_with_instrument)} note messages (" + f"processed {instr_msgs_processed} instrument messages, " + f"{sysex_msgs_processed} of which were recognized SysEx messages)") + + return timeline_with_instrument + + +def timeline_group_messages(timeline: List[AbsoluteTimeMessageWithInstrument]) -> List[ + AbsoluteCompleteNote]: + """ + Parse the timeline for note_on and note_off messages to determine the start and end + times of each note. + + :param timeline: A list of `AbsoluteTimeMessageWithInstrument` objects. + :return: A list of `AbsoluteCompleteNote` objects. + """ + # Find all note_on and note_on (velocity = 0) and note_off messages, and pair them + # up + logger.debug("Grouping messages into complete notes in the timeline") + + timeline_with_complete_notes = [] + highest_port = max([0] + [m.port for m in timeline]) + active_notes: Dict[Tuple[int, int], List[AbsoluteCompleteNote]] = { + (port, channel): [] + for port in range(highest_port + 1) + for channel in range(16) + } + last_time = 0 + + current_poly = 0 + max_poly = 0 + + for item in timeline: + msg = item.msg + port_and_channel = (item.port, msg.channel) + last_time = max(last_time, item.time) + + if msg.type == "note_on" and msg.velocity > 0: + # add to the list of playing notes + active_notes[port_and_channel].append( + AbsoluteCompleteNote( + start_time=item.time, + end_time=item.time, # will be updated when note_off found + note=msg.note, + velocity=msg.velocity, + instrument=item.instrument, + is_drum=item.is_drum, + ) + ) + current_poly += 1 + elif msg.type == "note_off" or (msg.type == "note_on" and msg.velocity == 0): + # find the playing note and finish it + for playing_note in active_notes[port_and_channel]: + if playing_note.note == msg.note: + playing_note.end_time = item.time + timeline_with_complete_notes.append(playing_note) + active_notes[port_and_channel].remove(playing_note) + current_poly -= 1 + break + else: + logger.warning(f"Could not find start of note for {item}") + max_poly = max(max_poly, current_poly) + + # handle hanging notes + hanging_count = 0 + for group_key in active_notes: + for playing_note in active_notes[group_key]: + playing_note.end_time = last_time + timeline_with_complete_notes.append(playing_note) + hanging_count += 1 + # no need to remove we're cleaning up + + # sort by start instead of when they ended + timeline_with_complete_notes.sort(key=lambda m: m.start_time) + + logger.debug(f"Global timeline has {len(timeline_with_complete_notes)} note events " + f"(maximum polyphony across all channels and ports was {max_poly} " + f"notes and had to clean up {hanging_count} hanging notes)") + + return timeline_with_complete_notes diff --git a/src/midi_to_song/timeline/processor.py b/src/midi_to_song/timeline/processor.py new file mode 100644 index 0000000..9fcd1cf --- /dev/null +++ b/src/midi_to_song/timeline/processor.py @@ -0,0 +1,264 @@ +import logging +from collections import defaultdict +from typing import Dict, List, Tuple + +from arcade.music_types import Song +from midi_to_song.models import AbsoluteCompleteChordWithTick, AbsoluteCompleteNote, \ + AbsoluteCompleteNoteWithTick +from utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +def timeline_quantize_to_song_ticks(timeline: List[AbsoluteCompleteNote], + song: Song) -> List[AbsoluteCompleteNoteWithTick]: + """ + Given the song's BPM and TPB, quantize the timeline's start and end times to ticks. + + :param timeline: A list of `AbsoluteCompleteNote` objects. + :param song: The `Song` object to use. + :return: A list of `AbsoluteCompleteNoteWithTick` objects. + """ + tick_time = (60 / song.beats_per_minute) / song.ticks_per_beat # in secs + logger.debug(f"Quantizing note times to ticks based of song BPM of " + f"{song.beats_per_minute} and TPB of {song.ticks_per_beat} - one tick " + f"is 1/{1 / tick_time} ({tick_time}) seconds long") + + res = [] + + for old_note in timeline: + new_start_tick = round(old_note.start_time / tick_time) + # Ensure all notes last for one tick + new_end_tick = max(round(old_note.end_time / tick_time), new_start_tick + 1) + res.append(AbsoluteCompleteNoteWithTick( + start_tick=new_start_tick, + end_tick=new_end_tick, + note=old_note.note, + velocity=old_note.velocity, + instrument=old_note.instrument, + is_drum=old_note.is_drum + )) + + return res + + +def find_all_melodic_instruments(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[ + int]: + """ + Search the timeline for all unique melodic instruments. + + :param timeline: A list of `AbsoluteTimeMessage` objects. + :return: A list of ints, representing what general MIDI instruments are in the song. + """ + logger.debug("Finding all melodic instruments in the timeline") + + return list(sorted(set([m.instrument for m in timeline if not m.is_drum]))) + + +def find_all_drum_notes_used(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[int]: + """ + Search the timeline for all unique drum notes. + + :param timeline: A list of `AbsoluteTimeMessage` objects. + :return: A list of ints, representing what general MIDI drum notes are in the song. + """ + logger.debug("Finding all drum notes in the timeline") + + return list(sorted(set([m.note for m in timeline if m.is_drum]))) + + +def find_all_drum_chords_used(timeline: List[AbsoluteCompleteChordWithTick]) -> List[ + int]: + """ + Search the timeline for all unique drum notes, for a list of chords. + + :param timeline: A list of `AbsoluteCompleteChordWithTick` objects. + :return: A list of ints, representing what general MIDI drum notes are in the song. + """ + logger.debug(f"Finding all drum chords in the timeline") + + return list( + sorted({note for chord in timeline if chord.is_drum for note in chord.notes})) + + +def timeline_group_by_instrument(timeline: List[AbsoluteCompleteNoteWithTick]) -> List[ + List[AbsoluteCompleteNoteWithTick]]: + """ + Split up the timeline by instrument. + + :param timeline: A list of `AbsoluteCompleteNote` objects. + :return: A list of lists of `AbsoluteCompleteNoteWithTick` objects. (Each list of + notes within the list have the same instrument) + """ + logger.debug("Splitting up the timeline by instruments") + + used_melodics = find_all_melodic_instruments(timeline) + used_drums = find_all_drum_notes_used(timeline) + logger.debug(f"Song used {len(used_melodics)} melodic instruments and " + f"{len(used_drums)} unique drum notes") + + tracks = [] + + for melodic in used_melodics: + tracks.append([note for note in timeline if + note.instrument == melodic and not note.is_drum]) + + if len(used_drums) > 0: + tracks.append([note for note in timeline if note.is_drum]) + + logger.debug(f"Split up global timeline into {len(tracks)} tracks") + + return tracks + + +def timeline_split_into_two_tracks_if_needed( + timeline: List[List[AbsoluteCompleteNoteWithTick]]) -> List[ + List[AbsoluteCompleteNoteWithTick]]: + """ + Go through the melodic tracks in the timeline and check the highest and lowest note + in each track. If it can't fit into one track (which has a range limit of 64 notes + from an octave offset) then we use two tracks and move notes as necessary. + + :param timeline: A list of lists of `AbsoluteCompleteNote` objects. + :return: A list of lists of `AbsoluteCompleteNoteWithTick` objects. + """ + logger.debug("Checking necessity to split track into two tracks for range") + + new_tracks: List[List[AbsoluteCompleteNoteWithTick]] = [] + + tracks_that_fit = 0 + tracks_that_split = 0 + + for old_track in timeline: + # drum tracks don't use octave offsets, only 61 samples max as well + if old_track[0].is_drum: + new_tracks.append(old_track) + tracks_that_fit += 1 + continue + + all_notes = [n.note for n in old_track] + highest_note = max(all_notes) + lowest_note = min(all_notes) + + def octave_offset_work(octave: int) -> bool: + return ((((octave - 2) * 12) <= lowest_note) and + (highest_note <= ((octave - 2) * 12 + 63))) + + # does ANY octave offset from [0, 9] work? + if any([octave_offset_work(o) for o in range(0, 10)]): + # we don't need to modify, when constructing the MakeCode Arcade Tracks, + # we'll find the correct octave offset again + new_tracks.append(old_track) + tracks_that_fit += 1 + else: + # split into two tracks, using octave offsets 0 and 6 guarantee covering the + # full shifted MIDI range + low_track = [n for n in old_track if n.note < 41] + high_track = [n for n in old_track if n.note >= 41] + new_tracks.append(low_track) + new_tracks.append(high_track) + tracks_that_split += 1 + + logger.debug(f"{tracks_that_fit} tracks fit within one track's range, " + f"{tracks_that_split} tracks had to split, total of {len(new_tracks)} " + f"tracks in timeline") + + return new_tracks + + +def timeline_group_into_perfect_chords( + timeline: List[List[AbsoluteCompleteNoteWithTick]]) -> List[ + List[AbsoluteCompleteChordWithTick]]: + """ + Go through the tracks in the timeline and group up notes that share the same start + and end tick and velocity and instruments to create "perfect" chords. + + :param timeline: A list of lists of `AbsoluteCompleteNoteWithTick` objects. + :return: A list of lists of `AbsoluteCompleteChordWithTick` objects. + """ + logger.debug("Grouping notes in timeline into perfect chords") + + new_tracks: List[List[AbsoluteCompleteChordWithTick]] = [] + + old_note_count = sum(len(track) for track in timeline) + + for old_track in timeline: + # use dictionaries to quickly find existing chords + # key is a tuple of (start_tick, end_tick, velocity, instrument, is_drum) + # values are notes in the chord + chords: Dict[Tuple[int, int, int, int, bool], List[int]] = defaultdict(list) + + for note in old_track: + key = (note.start_tick, note.end_tick, note.velocity, note.instrument, + note.is_drum) + # if a note has the same start and end tick and velocity and instrument + # they can be played as a chord + chords[key].append(note.note) + + new_track: List[AbsoluteCompleteChordWithTick] = [] + for (start_tick, end_tick, velocity, instrument, + is_drum), notes in chords.items(): + new_track.append(AbsoluteCompleteChordWithTick( + start_tick=start_tick, end_tick=end_tick, + notes=notes, + velocity=velocity, + instrument=instrument, is_drum=is_drum + )) + new_track.sort(key=lambda c: (c.start_tick, c.end_tick)) + new_tracks.append(new_track) + + new_chord_count = sum(len(track) for track in new_tracks) + + logger.debug(f"Created " + f"{sum([sum([1 if len(n.notes) > 1 else 0 for n in t]) for t in new_tracks])}" + f" multi-note chords (dropped from {old_note_count} notes to " + f"{new_chord_count} chords)") + + return new_tracks + + +def timeline_resolve_overlapping_chords( + timeline: List[List[AbsoluteCompleteChordWithTick]]) -> List[ + List[AbsoluteCompleteChordWithTick]]: + """ + Go through the melodic tracks in the timeline and check for chords overlapping in a + track. If they are, move them to an extra track. If there are no free tracks, create + one. This minimizes the number of extra tracks required while keeping the desired + polyphony of MIDI. + + :param timeline: A list of lists of `AbsoluteCompleteChordWithTick` objects. + :return: A list of lists of `AbsoluteCompleteChordWithTick` objects. + """ + logger.debug("Resolving overlapping chords") + + new_tracks: List[List[AbsoluteCompleteChordWithTick]] = [] + old_track_count = len(timeline) + + for old_track in timeline: + new_sub_tracks: List[List[AbsoluteCompleteChordWithTick]] = [[]] + + # don't have to worry about instrument matching because chords in old_track + # should all have the same instrument + for chord in old_track: + # try to place into a sub track + for sub_track in new_sub_tracks: + # if the last note in the subtrack has ended (or it's empty) + # TODO: Test if this needs to be < or <= works + # theoretically it should work fine with <= (and this will save tracks) + # but < will guarantee a "rest" + if len(sub_track) == 0 or sub_track[-1].end_tick < chord.start_tick: + sub_track.append(chord) + break + else: + # no free sub tracks, create + new_sub_tracks.append([chord]) + + # dump all generated sub tracks directly into the new timeline + new_tracks.extend(new_sub_tracks) + + new_track_count = len(new_tracks) + logger.debug(f"Created {new_track_count - old_track_count} extra tracks to handle " + f"overlapping chords (from {old_track_count} to {new_track_count} " + f"tracks)") + + return new_tracks diff --git a/src/midi_to_song/timeline/validation.py b/src/midi_to_song/timeline/validation.py new file mode 100644 index 0000000..3efbbd1 --- /dev/null +++ b/src/midi_to_song/timeline/validation.py @@ -0,0 +1,74 @@ +import logging +from typing import List + +from midi_to_song.models import AbsoluteCompleteChordWithTick +from utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +def timeline_checks(timeline: List[List[AbsoluteCompleteChordWithTick]]): + """ + Run some basic checks on the timeline to verify assumptions before mapping to the + MakeCode Arcade dataclasses. + + :param timeline: A list of lists of `AbsoluteCompleteChordWithTick` objects. + :raises ValueError: If any violations are detected. + """ + logger.debug("Running checks on the timeline") + # Track count must be less than 256 + if len(timeline) > 255: + raise ValueError(f"Too many tracks in the timeline! (max of 255, found " + f"{len(timeline)}) Reduce polyphony or unique instruments.") + for track in timeline: + if len(track) == 0: + raise Warning("Empty track in the timeline!") + # Sort by start tick as required, just in case + track.sort(key=lambda c: c.start_tick) + # The start and end ticks must be less than 65536 + highest_tick = max( + [chord.start_tick for chord in track] + [chord.end_tick for chord in track]) + if highest_tick > 65535: + raise ValueError(f"Chord start or end tick is too high! (max of 65535, " + f"found {highest_tick}) Decrease song length or reduce " + f"BPM/TPB at the cost of worse timing.") + # The highest and lowest notes must fit within 64 notes of an integer octave + # offset for melodic instruments + if not track[0].is_drum: + highest_note = max([max(chord.notes) for chord in track]) + lowest_note = min([min(chord.notes) for chord in track]) + + def octave_offset_work(octave: int) -> bool: + return ((((octave - 2) * 12) <= lowest_note) and + (highest_note <= ((octave - 2) * 12 + 63))) + + if not any([octave_offset_work(o) for o in range(0, 10)]): + raise ValueError(f"Track range too big to fit! (please report)") + # Chord must have less than 256 notes + max_chord_polyphony = max([len(chord.notes) for chord in track]) + if max_chord_polyphony > 255: + raise ValueError(f"Chord has too many notes! (max of 255, found " + f"{max_chord_polyphony})") + # Chords must last at least 1 tick + min_chord_duration = min([chord.end_tick - chord.start_tick for chord in track]) + if min_chord_duration < 1: + raise ValueError(f"Chord violated minimum duration time! (min of 1 tick, " + f"found {min_chord_duration} ticks, please report)") + # All chords must have the same instrument if melodic or all chords must be + # marked as drums in a drum track + if track[0].is_drum: + if any([not chord.is_drum for chord in track]): + raise ValueError(f"Found non drum chord in drum track! (please report") + else: + instruments = set([chord.instrument for chord in track]) + if len(instruments) > 1: + raise ValueError(f"Track has more than one instrument! (found " + f"{len(instruments)}, please report)") + # No chords overlap, i.e. start_tick >= end_tick of prev chord + for i in range(1, len(track)): + prev_chord = track[i - 1] + this_chord = track[i] + if prev_chord.end_tick > this_chord.start_tick: + raise ValueError(f"Chord overlapped! (please report)") + + logger.debug("Timeline passes all checks") diff --git a/src/utils/__init__.py b/src/utils/__init__.py new file mode 100644 index 0000000..e69de29 From 9eddc0ddaed4a93154a60f8a67ea03022ae25f31 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Sun, 31 May 2026 23:34:04 -0400 Subject: [PATCH 19/22] Add blank instrument parameters template YAML file --- example/blank_instrument_params.yaml | 450 +++++++++++++++++++++++++++ 1 file changed, 450 insertions(+) create mode 100644 example/blank_instrument_params.yaml diff --git a/example/blank_instrument_params.yaml b/example/blank_instrument_params.yaml new file mode 100644 index 0000000..6a3a3ca --- /dev/null +++ b/example/blank_instrument_params.yaml @@ -0,0 +1,450 @@ +_comment: " +Full GM1 support, minimal GM2 support (the extended GM2 drum notes, but only on +the standard kit), and very minimal Roland GS/Yamaha XG support (parsing very +specific SysEx messages) + +Units: + Attack, decay, release: milliseconds + Sustain and amplitudes: range 0-1024 + LFO frequencies: hz + +Waveforms map to as expected by MakeCode Arcade: + triangle = 1 + sawtooth = 2 + sine = 3 + noise_tunable = 4 + noise = 5 + square_10 = 11 + square_20 = 12 + square_30 = 13 + square_40 = 14 + square_50 (or square) = 15 + cycle_16 = 16 + cycle_32 = 17 + cycle_64 (or noise_tunable_2) = 18 + +https://arcade.makecode.com/developer/sound +> The noise is an actual white noise. It ignores the requested frequency or +> note. +> +> The tunable noise tone contains a pseudorandom sample. At high frequency it +> sounds similar to white noise, at low frequency it produces irregular +> crackling noises. +> +> The cycle 16, cycle 32, and cycle 64 waveforms are repeating pseudorandom +> patterns with a cycle length of 16/32/64 samples respectively. They sound +> like a noise-distorted square wave, with short cycles being more regular and +> long cycles being noisier. + +For testing instrument parameters, you can use this, by Richard +https://arcade.makecode.com/18336-29202-55655-79439 +" +melodic_instruments: + # Pianos + - instrument: 0 + _comment: Acoustic Grand Piano + waveform: triangle + amp_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + pitch_envelope: { attack: 0, decay: 0, sustain: 0, release: 0, amplitude: 0 } + amp_lfo: { frequency: 0, amplitude: 0 } + pitch_lfo: { frequency: 0, amplitude: 0 } + - instrument: 1 + _comment: Bright Acoustic Piano + - instrument: 2 + _comment: Electric Grand Piano + - instrument: 3 + _comment: Honky-tonk Piano + - instrument: 4 + _comment: Electric Piano 1 + - instrument: 5 + _comment: Electric Piano 2 + - instrument: 6 + _comment: Harpsichord + - instrument: 7 + _comment: Clavinet + # Chromatic percussion + - instrument: 8 + _comment: Celesta + - instrument: 9 + _comment: Glockenspiel + - instrument: 10 + _comment: Music Box + - instrument: 11 + _comment: Vibraphone + - instrument: 12 + _comment: Marimba + - instrument: 13 + _comment: Xylophone + - instrument: 14 + _comment: Tubular Bells + - instrument: 15 + _comment: Dulcimer + # Organs + - instrument: 16 + _comment: Drawbar Organ + - instrument: 17 + _comment: Percussive Organ + - instrument: 18 + _comment: Rock Organ + - instrument: 19 + _comment: Church Organ + - instrument: 20 + _comment: Reed Organ + - instrument: 21 + _comment: Accordion + - instrument: 22 + _comment: Harmonica + - instrument: 23 + _comment: Tango Accordion + # Guitars + - instrument: 24 + _comment: Acoustic Guitar (nylon) + - instrument: 25 + _comment: Acoustic Guitar (steel) + - instrument: 26 + _comment: Electric Guitar (jazz) + - instrument: 27 + _comment: Electric Guitar (clean) + - instrument: 28 + _comment: Electric Guitar (muted) + - instrument: 29 + _comment: Overdriven Guitar + - instrument: 30 + _comment: Distortion Guitar + - instrument: 31 + _comment: Guitar Harmonics + # Basses + - instrument: 32 + _comment: Acoustic Bass + - instrument: 33 + _comment: Electric Bass (finger) + - instrument: 34 + _comment: Electric Bass (pick) + - instrument: 35 + _comment: Fretless Bass + - instrument: 36 + _comment: Slap Bass 1 + - instrument: 37 + _comment: Slap Bass 2 + - instrument: 38 + _comment: Synth Bass 1 + - instrument: 39 + _comment: Synth Bass 2 + # Strings + - instrument: 40 + _comment: Violin + - instrument: 41 + _comment: Viola + - instrument: 42 + _comment: Cello + - instrument: 43 + _comment: Contrabass + - instrument: 44 + _comment: Tremolo Strings + - instrument: 45 + _comment: Pizzicato Strings + - instrument: 46 + _comment: Orchestral Harp + - instrument: 47 + _comment: Timpani + # Strings continued + - instrument: 48 + _comment: String Ensemble 1 + - instrument: 49 + _comment: String Ensemble 2 + - instrument: 50 + _comment: Synth Strings 1 + - instrument: 51 + _comment: Synth Strings 2 + - instrument: 52 + _comment: Choir Aahs + - instrument: 53 + _comment: Voice Oohs + - instrument: 54 + _comment: Synth Voice + - instrument: 55 + _comment: Orchestra Hit + # Brass + - instrument: 56 + _comment: Trumpet + - instrument: 57 + _comment: Trombone + - instrument: 58 + _comment: Tuba + - instrument: 59 + _comment: Muted Trumpet + - instrument: 60 + _comment: French Horn + - instrument: 61 + _comment: Brass Section + - instrument: 62 + _comment: Synth Brass 1 + - instrument: 63 + _comment: Synth Brass 2 + # Reeds + - instrument: 64 + _comment: Soprano Sax + - instrument: 65 + _comment: Alto Sax + - instrument: 66 + _comment: Tenor Sax + - instrument: 67 + _comment: Baritone Sax + - instrument: 68 + _comment: Oboe + - instrument: 69 + _comment: English Horn + - instrument: 70 + _comment: Bassoon + - instrument: 71 + _comment: Clarinet + # Pipes + - instrument: 72 + _comment: Piccolo + - instrument: 73 + _comment: Flute + - instrument: 74 + _comment: Recorder + - instrument: 75 + _comment: Pan Flute + - instrument: 76 + _comment: Blown Bottle + - instrument: 77 + _comment: Shakuhachi + - instrument: 78 + _comment: Whistle + - instrument: 79 + _comment: Ocarina + # Synth leads + - instrument: 80 + _comment: Lead 1 (square) + - instrument: 81 + _comment: Lead 2 (sawtooth) + - instrument: 82 + _comment: Lead 3 (calliope) + - instrument: 83 + _comment: Lead 4 (chiff) + - instrument: 84 + _comment: Lead 5 (charang) + - instrument: 85 + _comment: Lead 6 (voice) + - instrument: 86 + _comment: Lead 7 (fifths) + - instrument: 87 + _comment: Lead 8 (bass+lead) + # Synth pads + - instrument: 88 + _comment: Pad 1 (new age) + - instrument: 89 + _comment: Pad 2 (warm) + - instrument: 90 + _comment: Pad 3 (polysynth) + - instrument: 91 + _comment: Pad 4 (choir) + - instrument: 92 + _comment: Pad 5 (bowed) + - instrument: 93 + _comment: Pad 6 (metallic) + - instrument: 94 + _comment: Pad 7 (halo) + - instrument: 95 + _comment: Pad 8 (sweep) + # Synth sfx + - instrument: 96 + _comment: FX 1 (rain) + - instrument: 97 + _comment: FX 2 (soundtrack) + - instrument: 98 + _comment: FX 3 (crystal) + - instrument: 99 + _comment: FX 4 (atmosphere) + - instrument: 100 + _comment: FX 5 (brightness) + - instrument: 101 + _comment: FX 6 (goblins) + - instrument: 102 + _comment: FX 7 (echoes) + - instrument: 103 + _comment: FX 8 (sci-fi) + # Ethnic + - instrument: 104 + _comment: Sitar + - instrument: 105 + _comment: Banjo + - instrument: 106 + _comment: Shamisen + - instrument: 107 + _comment: Koto + - instrument: 108 + _comment: Kalimba + - instrument: 109 + _comment: Bag Pipe + - instrument: 110 + _comment: Fiddle + - instrument: 111 + _comment: Shanai + # Percussive + - instrument: 112 + _comment: Tinkle Bell + - instrument: 113 + _comment: Agogo + - instrument: 114 + _comment: Steel Drums + - instrument: 115 + _comment: Woodblock + - instrument: 116 + _comment: Taiko Drum + - instrument: 117 + _comment: Melodic Tom + - instrument: 118 + _comment: Synth Drum + # Sfx + - instrument: 119 + _comment: Reverse Cymbal + - instrument: 120 + _comment: Guitar Fret Noise + - instrument: 121 + _comment: Breath Noise + - instrument: 122 + _comment: Seashore + - instrument: 123 + _comment: Bird Tweet + - instrument: 124 + _comment: Telephone Ring + - instrument: 125 + _comment: Helicopter + - instrument: 126 + _comment: Applause + - instrument: 127 + _comment: Gunshot +drum_instruments: + - # GM2 start + - note: 27 + _comment: High Q + start_freq: 0 + start_vol: 0 + steps: + - { waveform: noise_tunable, target_freq: 0, target_vol: 0, duration: 0 } + - note: 28 + _comment: Slap + - note: 29 + _comment: Scratch Push + - note: 30 + _comment: Scratch Pull + - note: 31 + _comment: Sticks + - note: 32 + _comment: Square Click + - note: 33 + _comment: Metronome Click + - note: 34 + _comment: Metronome Bell + # GM2 end + - note: 35 + _comment: Bass Drum 2 + - note: 36 + _comment: Bass Drum 1 + - note: 37 + _comment: Side Stick + - note: 38 + _comment: Snare Drum 1 + - note: 39 + _comment: Hand Clap + - note: 40 + _comment: Snare Drum 2 + - note: 41 + _comment: Low Tom 2 + - note: 42 + _comment: Closed Hi-Hat + - note: 43 + _comment: Low Tom 1 + - note: 44 + _comment: Pedal Hi-Hat + - note: 45 + _comment: Mid Tom 2 + - note: 46 + _comment: Open Hi-Hat + - note: 47 + _comment: Mid Tom 1 + - note: 48 + _comment: High Tom 2 + - note: 49 + _comment: Crash Cymbal 1 + - note: 50 + _comment: High Tom 1 + - note: 51 + _comment: Ride Cymbal 1 + - note: 52 + _comment: Chinese Cymbal + - note: 53 + _comment: Ride Bell + - note: 54 + _comment: Tambourine + - note: 55 + _comment: Splash Cymbal + - note: 56 + _comment: Cowbell + - note: 57 + _comment: Crash Cymbal 2 + - note: 58 + _comment: Vibraslap + - note: 59 + _comment: Ride Cymbal 2 + - note: 60 + _comment: High Bongo + - note: 61 + _comment: Low Bongo + - note: 62 + _comment: Mute High Conga + - note: 63 + _comment: Open High Conga + - note: 64 + _comment: Low Conga + - note: 65 + _comment: High Timbale + - note: 66 + _comment: Low Timbale + - note: 67 + _comment: High Agogo + - note: 68 + _comment: Low Agogo + - note: 69 + _comment: Cabasa + - note: 70 + _comment: Maracas + - note: 71 + _comment: Short Whistle + - note: 72 + _comment: Long Whistle + - note: 73 + _comment: Short Guiro + - note: 74 + _comment: Long Guiro + - note: 75 + _comment: Claves + - note: 76 + _comment: High Wood Block + - note: 77 + _comment: Low Wood Block + - note: 78 + _comment: Mute Cuica + - note: 79 + _comment: Open Cuica + - note: 80 + _comment: Mute Triangle + - note: 81 + _comment: Open Triangle + # GM2 start + - note: 82 + _comment: Shaker + - note: 83 + _comment: Jingle Bell + - note: 84 + _comment: Belltree + - note: 85 + _comment: Castanets + - note: 86 + _comment: Mute Surdo + - note: 87 + _comment: Open Surdo + # GM2 end From 851f0a7ab300e51956132942f67aeab3fe497a41 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Mon, 1 Jun 2026 14:17:03 -0400 Subject: [PATCH 20/22] Overlapping chords don't need to be split into new tracks --- src/midi_to_song/__init__.py | 3 --- src/midi_to_song/timeline/validation.py | 6 ------ 2 files changed, 9 deletions(-) diff --git a/src/midi_to_song/__init__.py b/src/midi_to_song/__init__.py index c81b477..929edca 100644 --- a/src/midi_to_song/__init__.py +++ b/src/midi_to_song/__init__.py @@ -72,9 +72,6 @@ def convert_midi_to_song(midi_song: MidiFile, global_timeline: List[ List[AbsoluteCompleteChordWithTick]] = timeline_group_into_perfect_chords( global_timeline) - global_timeline: List[ - List[AbsoluteCompleteChordWithTick]] = timeline_resolve_overlapping_chords( - global_timeline) # Raises exceptions on check failures timeline_checks(global_timeline) diff --git a/src/midi_to_song/timeline/validation.py b/src/midi_to_song/timeline/validation.py index 3efbbd1..af40fcf 100644 --- a/src/midi_to_song/timeline/validation.py +++ b/src/midi_to_song/timeline/validation.py @@ -64,11 +64,5 @@ def octave_offset_work(octave: int) -> bool: if len(instruments) > 1: raise ValueError(f"Track has more than one instrument! (found " f"{len(instruments)}, please report)") - # No chords overlap, i.e. start_tick >= end_tick of prev chord - for i in range(1, len(track)): - prev_chord = track[i - 1] - this_chord = track[i] - if prev_chord.end_tick > this_chord.start_tick: - raise ValueError(f"Chord overlapped! (please report)") logger.debug("Timeline passes all checks") From e034a29a35c35fe25336c94fbb5d11169344868f Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Thu, 4 Jun 2026 21:57:24 -0400 Subject: [PATCH 21/22] Add testing options to help speed up instrument parameter development, and ensure attack < gate length + release in the amp envelope for all chords to avoid playback bug --- src/main.py | 58 ++++++++++++++- src/midi_to_song/__init__.py | 26 +++++-- src/midi_to_song/instruments.py | 99 ++++++++++++++----------- src/midi_to_song/models.py | 13 +++- src/midi_to_song/timeline/processor.py | 54 ++++++++++++++ src/midi_to_song/timeline/validation.py | 30 +++++++- 6 files changed, 223 insertions(+), 57 deletions(-) diff --git a/src/main.py b/src/main.py index af28641..6abff0c 100644 --- a/src/main.py +++ b/src/main.py @@ -5,8 +5,9 @@ from mido import MidiFile from arcade.music import encode_song_to_hex -from midi_to_song import convert_midi_to_song +from midi_to_song import TestingOptionsForMIDIToSong, convert_midi_to_song from midi_to_song.instruments import load_instrument_params +from midi_to_song.models import TestingOptionsForLoadInstrumentParams from utils.logger import create_logger, set_all_stdout_logger_levels parser = ArgumentParser(description="Convert a MIDI file to a MakeCode Arcade song.") @@ -22,11 +23,43 @@ const=logging.DEBUG, default=logging.INFO, help="Include debug messages. Defaults to info and " "greater severity messages only.") + +testing_group = parser.add_argument_group("Testing options") +testing_group.add_argument("--test-replace-all-melodics-with", type=int, + default=None, help="Replace all melodic tracks in a song " + "with the specific MIDI instrument.") +testing_group.add_argument("--test-ask-to-replace-all-melodics-with", + action="store_true", help="Prompt the user to replace all " + "melodic tracks in a song with a " + "specific MIDI instrument.") +testing_group.add_argument("--test-generate-code", action="store_true", + help="Generate the MakeCode Arcade code to play the song.") +testing_group.add_argument("--test-force-instrument-param-load", action="store_true", + help="Forcibly load the instrument parameter mapping file, " + "even if it would normally cause errors.") + args = parser.parse_args() logger = create_logger(name=__name__, level=logging.INFO) set_all_stdout_logger_levels(args.debug) logger.debug(f"Received arguments: {args}") +testing_opts_midi_to_song = TestingOptionsForMIDIToSong() +testing_opts_load_instrument_params = TestingOptionsForLoadInstrumentParams() + +testing_opts_midi_to_song.replace_all_melodics_with = args.test_replace_all_melodics_with +testing_opts_load_instrument_params.force_load = args.test_force_instrument_param_load +if args.test_ask_to_replace_all_melodics_with: + testing_opts_midi_to_song.replace_all_melodics_with = int( + input("Replace all melodics with MIDI " + "instrument: ")) +testing_opts_midi_to_song.generate_code = args.test_generate_code +if testing_opts_midi_to_song.replace_all_melodics_with is not None: + logger.info(f"Replacing all melodic instruments with MIDI instrument " + f"{testing_opts_midi_to_song.replace_all_melodics_with} in final output") +if testing_opts_midi_to_song.generate_code: + logger.info(f"Final result will be valid MakeCode Arcade TypeScript code to play " + f"the song") + input_path = Path(args.input) logger.info(f"Reading MIDI file from {input_path}") @@ -34,21 +67,38 @@ logger.debug(f"Found {len(mid.tracks)} tracks, length of {mid.length}s") input_instrument_param_path = Path(args.input_instrument_params) -logger.info(f"Reading instrument parameter mapping from {input_instrument_param_path}") +if testing_opts_load_instrument_params.force_load: + logger.info(f"Forcibly reading instrument params from " + f"{input_instrument_param_path}") +else: + logger.info( + f"Reading instrument parameter mapping from {input_instrument_param_path}") -mapping = load_instrument_params(input_instrument_param_path.read_text()) +mapping = load_instrument_params(input_instrument_param_path.read_text(), + testing_opts_load_instrument_params) logger.debug(f"Mapped {len(mapping.melodic_instruments)} melodic instruments and " f"{len(mapping.drum_instruments)} drum instruments") -song = convert_midi_to_song(mid, mapping) +song = convert_midi_to_song(mid, mapping, testing_opts_midi_to_song) h = encode_song_to_hex(song) final_output = f"hex`{h}`" logger.info("Finished converting MIDI file") +if testing_opts_midi_to_song.generate_code: + final_output = f"""music.play(music.createSong( + {final_output} +), music.PlaybackMode.UntilDone); +""" +if testing_opts_midi_to_song.replace_all_melodics_with is not None: + final_output = (f"// melodics replaced with MIDI instrument " + f"{testing_opts_midi_to_song.replace_all_melodics_with}\n{final_output}") + output_path = Path(args.output) if args.output is not None else None if output_path is not None: logger.info(f"Writing result to {output_path}") output_path.write_text(final_output) else: + if testing_opts_midi_to_song.generate_code: + final_output = "\n" + final_output logger.info(f"Writing result to stdout") print(final_output) diff --git a/src/midi_to_song/__init__.py b/src/midi_to_song/__init__.py index 929edca..fdee2ed 100644 --- a/src/midi_to_song/__init__.py +++ b/src/midi_to_song/__init__.py @@ -1,7 +1,7 @@ import logging from copy import deepcopy from math import ceil -from typing import Dict, List +from typing import Dict, List, Optional from mido import MidiFile @@ -10,11 +10,13 @@ from midi_to_song.instruments import InstrumentParameterMapping from midi_to_song.models import AbsoluteCompleteChordWithTick, AbsoluteCompleteNote, \ AbsoluteCompleteNoteWithTick, AbsoluteTickMessage, AbsoluteTimeMessage, \ - AbsoluteTimeMessageWithInstrument, ChannelState, DrumDeterminationSource + AbsoluteTimeMessageWithInstrument, ChannelState, DrumDeterminationSource, \ + TestingOptionsForMIDIToSong from midi_to_song.timeline.parser import timeline_build, timeline_find_instrument_data, \ timeline_group_messages from midi_to_song.timeline.processor import find_all_drum_chords_used, \ - timeline_group_by_instrument, timeline_group_into_perfect_chords, \ + timeline_fix_gate_lens, timeline_group_by_instrument, \ + timeline_group_into_perfect_chords, \ timeline_quantize_to_song_ticks, timeline_resolve_overlapping_chords, \ timeline_split_into_two_tracks_if_needed from midi_to_song.timeline.validation import timeline_checks @@ -24,17 +26,23 @@ def convert_midi_to_song(midi_song: MidiFile, - mapping: InstrumentParameterMapping) -> Song: + mapping: InstrumentParameterMapping, + testing_opts: Optional[ + TestingOptionsForMIDIToSong] = None) -> Song: """ Convert a MIDI file into a MakeCode Arcade song. :param midi_song: A `MidiFile` object. :param mapping: An `InstrumentParameterMapping` object, loaded from `load_instrument_params`. + :param testing_opts: Extra options used for testing, passed from the CLI. :return: MakeCode Arcade `Song` object. """ logger.debug("Converting MIDI file into MakeCode Arcade song") + if testing_opts is None: + testing_opts = TestingOptionsForMIDIToSong() + song = Song( measures=1, beats_per_measure=4, @@ -52,6 +60,13 @@ def convert_midi_to_song(midi_song: MidiFile, global_timeline: List[AbsoluteCompleteNote] = timeline_group_messages( global_timeline) + if testing_opts.replace_all_melodics_with is not None: + logger.debug(f"Testing option enabled to replace all melodic instruments with " + f"MIDI instrument {testing_opts.replace_all_melodics_with}") + for m in global_timeline: + if not m.is_drum: + m.instrument = testing_opts.replace_all_melodics_with + # MIDI file with C4 (MIDI 60) plays at B5 (MIDI 83) # This is because MakeCode Arcade defines C4 as 49 instead of 60 # And now I have no idea why I need to shift down another octave but then it works @@ -62,6 +77,7 @@ def convert_midi_to_song(midi_song: MidiFile, note.note -= 11 # MIDI 60 (C4) maps to Arcade's C4 of 49 note.note -= 12 # another octave down makes it correct + global_timeline = timeline_fix_gate_lens(global_timeline, song, mapping) global_timeline: List[ AbsoluteCompleteNoteWithTick] = timeline_quantize_to_song_ticks(global_timeline, song) @@ -74,7 +90,7 @@ def convert_midi_to_song(midi_song: MidiFile, global_timeline) # Raises exceptions on check failures - timeline_checks(global_timeline) + timeline_checks(song, global_timeline, mapping) # With all this pitch checks and timing manipulations done to fit MakeCode Arcade's # song's constraints, we should be able to basically map 1-1 to the MakeCode Arcade diff --git a/src/midi_to_song/instruments.py b/src/midi_to_song/instruments.py index 1fabb54..36b9fd3 100644 --- a/src/midi_to_song/instruments.py +++ b/src/midi_to_song/instruments.py @@ -1,11 +1,12 @@ import logging from dataclasses import dataclass from enum import IntEnum -from typing import Dict +from typing import Dict, Optional from yaml import safe_load from arcade.music_types import DrumInstrument, DrumSoundStep, Envelope, Instrument, LFO +from midi_to_song.models import TestingOptionsForLoadInstrumentParams from utils.logger import create_logger logger = create_logger(name=__name__, level=logging.INFO) @@ -64,13 +65,16 @@ def waveform_from_str(w: str) -> WaveForm: }[w] -def load_instrument_params(yaml_text: str) -> InstrumentParameterMapping: +def load_instrument_params(yaml_text: str, + testing_opts: Optional[ + TestingOptionsForLoadInstrumentParams] = None) -> InstrumentParameterMapping: """ Take a YAML file specifying instrument data and return an instrument parameter mapping. For melodic instruments, the octave has been set to 0, duplicate as necessary for the full MIDI range (use octave offset 2 and 7 for full range) :param yaml_text: String holding the YAML file's text. + :param testing_opts: Extra options used for testing, passed from the CLI. :return: An `InstrumentParameterMapping` object. """ logger.debug(f"Loading instrument parameters from {len(yaml_text)} characters of " @@ -82,52 +86,59 @@ def load_instrument_params(yaml_text: str) -> InstrumentParameterMapping: logger.debug(f"Creating mappings for {len(data["melodic_instruments"])} melodic " f"instruments") for instr in data["melodic_instruments"]: - # TODO: If pitch envelope or LFOs aren't defined in the YAML don't error out - mapping.melodic_instruments[instr["instrument"]] = Instrument( - - waveform=waveform_from_str(instr["waveform"]), - # Each note can range from 0-63, so we'll have two tracks, each with the - # same two instruments but at different octave offsets - octave=0, - amp_envelope=Envelope( - attack=instr["amp_envelope"]["attack"], - decay=instr["amp_envelope"]["decay"], - sustain=instr["amp_envelope"]["sustain"], - release=instr["amp_envelope"]["release"], - amplitude=instr["amp_envelope"]["amplitude"], - ), - pitch_envelope=Envelope( - attack=instr["pitch_envelope"]["attack"], - decay=instr["pitch_envelope"]["decay"], - sustain=instr["pitch_envelope"]["sustain"], - release=instr["pitch_envelope"]["release"], - amplitude=instr["pitch_envelope"]["amplitude"], - ), - amp_lfo=LFO( - frequency=instr["amp_lfo"]["frequency"], - amplitude=instr["amp_lfo"]["amplitude"], - ), - pitch_lfo=LFO( - frequency=instr["pitch_lfo"]["frequency"], - amplitude=instr["pitch_lfo"]["amplitude"], + try: + # TODO: If pitch envelope or LFOs aren't defined in the YAML don't error out + mapping.melodic_instruments[instr["instrument"]] = Instrument( + waveform=waveform_from_str(instr["waveform"]), + # Each note can range from 0-63, so we'll have two tracks, each with the + # same two instruments but at different octave offsets + octave=0, + amp_envelope=Envelope( + attack=instr["amp_envelope"]["attack"], + decay=instr["amp_envelope"]["decay"], + sustain=instr["amp_envelope"]["sustain"], + release=instr["amp_envelope"]["release"], + amplitude=instr["amp_envelope"]["amplitude"], + ), + pitch_envelope=Envelope( + attack=instr["pitch_envelope"]["attack"], + decay=instr["pitch_envelope"]["decay"], + sustain=instr["pitch_envelope"]["sustain"], + release=instr["pitch_envelope"]["release"], + amplitude=instr["pitch_envelope"]["amplitude"], + ), + amp_lfo=LFO( + frequency=instr["amp_lfo"]["frequency"], + amplitude=instr["amp_lfo"]["amplitude"], + ), + pitch_lfo=LFO( + frequency=instr["pitch_lfo"]["frequency"], + amplitude=instr["pitch_lfo"]["amplitude"], + ) ) - ) + except Exception as e: + if not testing_opts.force_load: + raise e logger.debug(f"Creating mappings for {len(data["drum_instruments"])} drum " f"instruments") for sample in data["drum_instruments"]: - mapping.drum_instruments[sample["note"]] = DrumInstrument( - name=sample["_comment"], - start_frequency=sample["start_freq"], - start_volume=sample["start_vol"], - steps=[ - DrumSoundStep( - waveform=waveform_from_str(step["waveform"]), - frequency=step["target_freq"], - volume=step["target_vol"], - duration=step["duration"], - ) for step in sample["steps"] - ] - ) + try: + mapping.drum_instruments[sample["note"]] = DrumInstrument( + name=sample["_comment"], + start_frequency=sample["start_freq"], + start_volume=sample["start_vol"], + steps=[ + DrumSoundStep( + waveform=waveform_from_str(step["waveform"]), + frequency=step["target_freq"], + volume=step["target_vol"], + duration=step["duration"], + ) for step in sample["steps"] + ] + ) + except Exception as e: + if not testing_opts.force_load: + raise e return mapping diff --git a/src/midi_to_song/models.py b/src/midi_to_song/models.py index 84e9fbd..ec11ce7 100644 --- a/src/midi_to_song/models.py +++ b/src/midi_to_song/models.py @@ -1,7 +1,7 @@ import logging from dataclasses import dataclass from enum import IntEnum -from typing import List +from typing import List, Optional from mido import Message @@ -86,3 +86,14 @@ class AbsoluteCompleteChordWithTick: instrument: int is_drum: bool + + +@dataclass +class TestingOptionsForLoadInstrumentParams: + force_load: Optional[bool] = False + + +@dataclass +class TestingOptionsForMIDIToSong: + replace_all_melodics_with: Optional[int] = None + generate_code: Optional[bool] = False diff --git a/src/midi_to_song/timeline/processor.py b/src/midi_to_song/timeline/processor.py index 9fcd1cf..e62114f 100644 --- a/src/midi_to_song/timeline/processor.py +++ b/src/midi_to_song/timeline/processor.py @@ -1,8 +1,11 @@ import logging from collections import defaultdict +from copy import deepcopy +from math import ceil from typing import Dict, List, Tuple from arcade.music_types import Song +from midi_to_song import InstrumentParameterMapping from midi_to_song.models import AbsoluteCompleteChordWithTick, AbsoluteCompleteNote, \ AbsoluteCompleteNoteWithTick from utils.logger import create_logger @@ -10,6 +13,57 @@ logger = create_logger(name=__name__, level=logging.INFO) +def timeline_fix_gate_lens(timeline: List[AbsoluteCompleteNote], + song: Song, + mapping: InstrumentParameterMapping) -> List[ + AbsoluteCompleteNote]: + """ + Increase all the durations of the notes to ensure that attack < gate len + release, + so a playback bug is avoided. See https://github.com/microsoft/pxt/pull/11352. + + :param timeline: A list of `AbsoluteCompleteNote` objects. + :param song: The `Song` object to reference the TPM and BPM. + :param mapping: An `InstrumentParameterMapping` object, loaded from + `load_instrument_params`. + :return: A list of `AbsoluteCompleteNote` objects. + """ + logger.debug("Fixing gate lengths of notes in the timeline to avoid playback bug") + + seconds_per_tick = (60 / song.beats_per_measure) / song.ticks_per_beat + + res = [] + + durations_extended = 0 + + for old_note in timeline: + if old_note.is_drum: + # drum instruments don't have gate lengths, so we can skip + res.append(old_note) + continue + instrument_params = mapping.melodic_instruments[old_note.instrument] + attack = instrument_params.amp_envelope.attack / 1000 + release = instrument_params.amp_envelope.release / 1000 + old_duration = old_note.end_time - old_note.start_time + min_duration = attack - release + if old_duration <= min_duration: + min_ticks = min_duration / seconds_per_tick + required_ticks = ceil(min_ticks) + if min_ticks == required_ticks: + required_ticks += 1 + new_note = deepcopy(old_note) + new_duration = required_ticks * seconds_per_tick + new_note.end_time = new_note.start_time + new_duration + durations_extended += 1 + else: + new_note = old_note + res.append(new_note) + + logger.debug(f"Extended {durations_extended} note durations to ensure duration > " + f"attack - release") + + return res + + def timeline_quantize_to_song_ticks(timeline: List[AbsoluteCompleteNote], song: Song) -> List[AbsoluteCompleteNoteWithTick]: """ diff --git a/src/midi_to_song/timeline/validation.py b/src/midi_to_song/timeline/validation.py index af40fcf..619405f 100644 --- a/src/midi_to_song/timeline/validation.py +++ b/src/midi_to_song/timeline/validation.py @@ -1,18 +1,26 @@ import logging from typing import List +from arcade.music_types import Song +from midi_to_song import InstrumentParameterMapping from midi_to_song.models import AbsoluteCompleteChordWithTick from utils.logger import create_logger logger = create_logger(name=__name__, level=logging.INFO) -def timeline_checks(timeline: List[List[AbsoluteCompleteChordWithTick]]): +def timeline_checks(song: Song, + timeline: List[List[AbsoluteCompleteChordWithTick]], + mapping: InstrumentParameterMapping): """ Run some basic checks on the timeline to verify assumptions before mapping to the MakeCode Arcade dataclasses. + :param song: The MakeCode Arcade `Song` object that will be added to, with the + correctly configured BPM and TPM. :param timeline: A list of lists of `AbsoluteCompleteChordWithTick` objects. + :param mapping: An `InstrumentParameterMapping` object, loaded from + `load_instrument_params`. :raises ValueError: If any violations are detected. """ logger.debug("Running checks on the timeline") @@ -23,6 +31,7 @@ def timeline_checks(timeline: List[List[AbsoluteCompleteChordWithTick]]): for track in timeline: if len(track) == 0: raise Warning("Empty track in the timeline!") + track_is_drum = track[0].is_drum # Sort by start tick as required, just in case track.sort(key=lambda c: c.start_tick) # The start and end ticks must be less than 65536 @@ -34,7 +43,7 @@ def timeline_checks(timeline: List[List[AbsoluteCompleteChordWithTick]]): f"BPM/TPB at the cost of worse timing.") # The highest and lowest notes must fit within 64 notes of an integer octave # offset for melodic instruments - if not track[0].is_drum: + if not track_is_drum: highest_note = max([max(chord.notes) for chord in track]) lowest_note = min([min(chord.notes) for chord in track]) @@ -56,7 +65,7 @@ def octave_offset_work(octave: int) -> bool: f"found {min_chord_duration} ticks, please report)") # All chords must have the same instrument if melodic or all chords must be # marked as drums in a drum track - if track[0].is_drum: + if track_is_drum: if any([not chord.is_drum for chord in track]): raise ValueError(f"Found non drum chord in drum track! (please report") else: @@ -64,5 +73,20 @@ def octave_offset_work(octave: int) -> bool: if len(instruments) > 1: raise ValueError(f"Track has more than one instrument! (found " f"{len(instruments)}, please report)") + # Ensure attack < gate length + release in the amp envelope for all chords, + # otherwise a bug is triggered and the simulator freezes (see + # https://github.com/microsoft/pxt/pull/11352) + if not track_is_drum: + instrument = mapping.melodic_instruments[track[0].instrument] + attack = instrument.amp_envelope.attack + release = instrument.amp_envelope.release + ms_per_tick = (60 / song.beats_per_minute) / song.ticks_per_beat * 1000 + chord_times = [ + round((chord.end_tick - chord.start_tick) * ms_per_tick) for + chord in track] + if any([(not (attack < (chord_time + release))) for chord_time in + chord_times]): + raise ValueError("Found chord where instrument's attack time >= " + "gate length + release time! (please report)") logger.debug("Timeline passes all checks") From 516d1bd703878123f49a57e17ed1ffe31629d4a6 Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Thu, 4 Jun 2026 22:27:38 -0400 Subject: [PATCH 22/22] Can generate melodic samples to easily listen to a bunch of instruments in a row --- src/main.py | 45 +++++++++++++++++++++++++++++++++++--------- src/utils/strings.py | 37 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 src/utils/strings.py diff --git a/src/main.py b/src/main.py index 6abff0c..a7b251c 100644 --- a/src/main.py +++ b/src/main.py @@ -9,6 +9,7 @@ from midi_to_song.instruments import load_instrument_params from midi_to_song.models import TestingOptionsForLoadInstrumentParams from utils.logger import create_logger, set_all_stdout_logger_levels +from utils.strings import parse_range parser = ArgumentParser(description="Convert a MIDI file to a MakeCode Arcade song.") parser.add_argument("--input", "-i", type=Path, required=True, @@ -37,6 +38,14 @@ testing_group.add_argument("--test-force-instrument-param-load", action="store_true", help="Forcibly load the instrument parameter mapping file, " "even if it would normally cause errors.") +testing_group.add_argument("--test-sample-melodic-instruments", type=parse_range, + help="Pass in a range of MIDI instruments to sample, such " + "as \"0,2,4-10\" to replicate the song several times " + "and replace all melodic tracks in the song with the " + "specific MIDI instrument. Basically does what " + "`--test-replace-all-melodics-with` and " + "`--test-generate-code` but with a bunch of specified " + "instruments.") args = parser.parse_args() logger = create_logger(name=__name__, level=logging.INFO) @@ -79,19 +88,37 @@ logger.debug(f"Mapped {len(mapping.melodic_instruments)} melodic instruments and " f"{len(mapping.drum_instruments)} drum instruments") -song = convert_midi_to_song(mid, mapping, testing_opts_midi_to_song) -h = encode_song_to_hex(song) -final_output = f"hex`{h}`" -logger.info("Finished converting MIDI file") +melodic_sample = args.test_sample_melodic_instruments +if melodic_sample is not None: + logger.info(f"Sampling {melodic_sample} melodic instruments") -if testing_opts_midi_to_song.generate_code: - final_output = f"""music.play(music.createSong( + final_output = "\n// generated melodic instrument sample\n\n" + + for instrument in melodic_sample: + logger.info(f"Generating code for melodic instrument {instrument}") + testing_opts_midi_to_song.replace_all_melodics_with = instrument + song = convert_midi_to_song(mid, mapping, testing_opts_midi_to_song) + h = encode_song_to_hex(song) + final_output += f"""// melodics replaced with MIDI instrument {instrument} +info.setScore({instrument}); +music.play(music.createSong( + hex`{h}` +), music.PlaybackMode.UntilDone); +""" +else: + song = convert_midi_to_song(mid, mapping, testing_opts_midi_to_song) + h = encode_song_to_hex(song) + final_output = f"hex`{h}`" + logger.info("Finished converting MIDI file") + + if testing_opts_midi_to_song.generate_code: + final_output = f"""music.play(music.createSong( {final_output} ), music.PlaybackMode.UntilDone); """ -if testing_opts_midi_to_song.replace_all_melodics_with is not None: - final_output = (f"// melodics replaced with MIDI instrument " - f"{testing_opts_midi_to_song.replace_all_melodics_with}\n{final_output}") + if testing_opts_midi_to_song.replace_all_melodics_with is not None: + final_output = (f"// melodics replaced with MIDI instrument " + f"{testing_opts_midi_to_song.replace_all_melodics_with}\n{final_output}") output_path = Path(args.output) if args.output is not None else None if output_path is not None: diff --git a/src/utils/strings.py b/src/utils/strings.py new file mode 100644 index 0000000..181d3f2 --- /dev/null +++ b/src/utils/strings.py @@ -0,0 +1,37 @@ +import argparse + + +# Thanks Gemini +def parse_range(value): + """ + Parses a string of numbers, ranges, or a mix (e.g., '0-30' or '0,2,4,10-30') + and returns a sorted list of unique integers. + """ + result = set() + + # Split by commas to handle individual numbers or sub-ranges + for part in value.split(','): + part = part.strip() + if not part: + continue + + # Check if it's a range (e.g., 10-30) + if '-' in part: + try: + start, end = map(int, part.split('-')) + if start > end: + raise argparse.ArgumentTypeError( + f"Invalid range: {part} (start cannot be greater than end)") + # +1 to make the end inclusive, which is standard for CLI ranges + result.update(range(start, end + 1)) + except ValueError: + raise argparse.ArgumentTypeError( + f"Invalid range format: '{part}'. Expected 'start-end'.") + else: + # It's a single number + try: + result.add(int(part)) + except ValueError: + raise argparse.ArgumentTypeError(f"Invalid integer: '{part}'") + + return sorted(list(result))