From 248ba483c6027223bfa11a74ee6eeacd55606957 Mon Sep 17 00:00:00 2001 From: "5684185+vsariola@users.noreply.github.com" <5684185+vsariola@users.noreply.github.com> Date: Thu, 6 Jul 2023 23:47:55 +0300 Subject: [PATCH] feat: add ability to import 4klang patches and instruments --- 4klang.go | 519 ++++++++++++++++++ examples/fourklang/2010_ergon_5.4kp | Bin 0 -> 18440 bytes examples/fourklang/LightRythm.4kp | Bin 0 -> 18440 bytes examples/fourklang/baghdad.4kp | Bin 0 -> 18440 bytes examples/fourklang/bf-enlighten.4kp | Bin 0 -> 18440 bytes examples/fourklang/c0c00n_001.4kp | Bin 0 -> 18440 bytes examples/fourklang/dollop.4kp | Bin 0 -> 18440 bytes examples/fourklang/example.4kp | Bin 0 -> 18440 bytes examples/fourklang/example2.4kp | Bin 0 -> 18440 bytes examples/fourklang/kevinspacy.4kp | Bin 0 -> 18440 bytes examples/fourklang/punqtured-sundowner.4kp | Bin 0 -> 18440 bytes examples/fourklang/untitled2.4kp | Bin 0 -> 18440 bytes .../fourklang/virgill - 4klang basics.4kp | Bin 0 -> 18440 bytes examples/fourklang_instruments/BA_Dark.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/BA_DarkChorus.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/BA_Deepness.4ki | Bin 0 -> 1092 bytes .../BA_DirectPunchMS.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/BA_Mighty.4ki | Bin 0 -> 1092 bytes .../BA_Mighty_Feedback.4ki | Bin 0 -> 1092 bytes .../BA_NotFromThisWorld.4ki | Bin 0 -> 1092 bytes .../BA_NotFromThisWorld2.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/BA_SawBass.4ki | Bin 0 -> 1092 bytes .../BA_SawBassFlanger.4ki | Bin 0 -> 1092 bytes .../GA_RestInPeaceMS.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/KY_GarageOrgan.4ki | Bin 0 -> 1092 bytes .../KY_GarageOrganChorus.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/KY_Lullaby.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/KY_Lullaby2.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/KY_Rhodes.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/LD_AlphaOmegaMS.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/LD_Farscape.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/LD_More&MoreMS.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/LD_Morpher.4ki | Bin 0 -> 1092 bytes .../LD_RestInPeaceMS.4ki | Bin 0 -> 1092 bytes .../LD_Short&PunchyMS.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/PA_Fairies.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/PA_Jarresque.4ki | Bin 0 -> 1092 bytes .../PA_JarresqueChorus.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/PA_LoFiChoir.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/PA_LongPad.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/PA_Minorium.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/PA_Strangeland.4ki | Bin 0 -> 1092 bytes .../PA_StrangelandChorus.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/PA_SynastasiaMS.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/SY_RandomArp.4ki | Bin 0 -> 1092 bytes .../SY_RandomArpFlanger.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/airy.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/basedrum.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/basedrum2.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/basedrum3.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/basedrum4.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/bass.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/bass2.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/clap.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/guitar.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/guitar2.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/hihat.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/hihat2.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/pad.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/pad2.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/piano.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/piano2.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/rimshot.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/snare.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/snare2.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/snare3.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/snare4.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/snare5.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/snare6.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/string.4ki | Bin 0 -> 1092 bytes examples/fourklang_instruments/synth.4ki | Bin 0 -> 1092 bytes .../fourklang_instruments/synthFlanger.4ki | Bin 0 -> 1092 bytes patch.go | 12 + synth.go | 6 +- tracker/gioui/filedialog.go | 4 +- tracker/gioui/files.go | 34 +- tracker/gioui/layout.go | 2 + tracker/model.go | 41 +- tracker/player.go | 9 +- unittype.go | 2 +- vm/bytepatch.go | 6 +- vm/compiler/bridge/bridge.go | 12 +- vm/compiler/bridge/bridge_test.go | 10 +- vm/compiler/compiler.go | 2 +- vm/delaytable.go | 17 +- vm/interpreter.go | 18 +- vm/interpreter_test.go | 4 +- 87 files changed, 643 insertions(+), 55 deletions(-) create mode 100644 4klang.go create mode 100644 examples/fourklang/2010_ergon_5.4kp create mode 100644 examples/fourklang/LightRythm.4kp create mode 100644 examples/fourklang/baghdad.4kp create mode 100644 examples/fourklang/bf-enlighten.4kp create mode 100644 examples/fourklang/c0c00n_001.4kp create mode 100644 examples/fourklang/dollop.4kp create mode 100644 examples/fourklang/example.4kp create mode 100644 examples/fourklang/example2.4kp create mode 100644 examples/fourklang/kevinspacy.4kp create mode 100644 examples/fourklang/punqtured-sundowner.4kp create mode 100644 examples/fourklang/untitled2.4kp create mode 100644 examples/fourklang/virgill - 4klang basics.4kp create mode 100644 examples/fourklang_instruments/BA_Dark.4ki create mode 100644 examples/fourklang_instruments/BA_DarkChorus.4ki create mode 100644 examples/fourklang_instruments/BA_Deepness.4ki create mode 100644 examples/fourklang_instruments/BA_DirectPunchMS.4ki create mode 100644 examples/fourklang_instruments/BA_Mighty.4ki create mode 100644 examples/fourklang_instruments/BA_Mighty_Feedback.4ki create mode 100644 examples/fourklang_instruments/BA_NotFromThisWorld.4ki create mode 100644 examples/fourklang_instruments/BA_NotFromThisWorld2.4ki create mode 100644 examples/fourklang_instruments/BA_SawBass.4ki create mode 100644 examples/fourklang_instruments/BA_SawBassFlanger.4ki create mode 100644 examples/fourklang_instruments/GA_RestInPeaceMS.4ki create mode 100644 examples/fourklang_instruments/KY_GarageOrgan.4ki create mode 100644 examples/fourklang_instruments/KY_GarageOrganChorus.4ki create mode 100644 examples/fourklang_instruments/KY_Lullaby.4ki create mode 100644 examples/fourklang_instruments/KY_Lullaby2.4ki create mode 100644 examples/fourklang_instruments/KY_Rhodes.4ki create mode 100644 examples/fourklang_instruments/LD_AlphaOmegaMS.4ki create mode 100644 examples/fourklang_instruments/LD_Farscape.4ki create mode 100644 examples/fourklang_instruments/LD_More&MoreMS.4ki create mode 100644 examples/fourklang_instruments/LD_Morpher.4ki create mode 100644 examples/fourklang_instruments/LD_RestInPeaceMS.4ki create mode 100644 examples/fourklang_instruments/LD_Short&PunchyMS.4ki create mode 100644 examples/fourklang_instruments/PA_Fairies.4ki create mode 100644 examples/fourklang_instruments/PA_Jarresque.4ki create mode 100644 examples/fourklang_instruments/PA_JarresqueChorus.4ki create mode 100644 examples/fourklang_instruments/PA_LoFiChoir.4ki create mode 100644 examples/fourklang_instruments/PA_LongPad.4ki create mode 100644 examples/fourklang_instruments/PA_Minorium.4ki create mode 100644 examples/fourklang_instruments/PA_Strangeland.4ki create mode 100644 examples/fourklang_instruments/PA_StrangelandChorus.4ki create mode 100644 examples/fourklang_instruments/PA_SynastasiaMS.4ki create mode 100644 examples/fourklang_instruments/SY_RandomArp.4ki create mode 100644 examples/fourklang_instruments/SY_RandomArpFlanger.4ki create mode 100644 examples/fourklang_instruments/airy.4ki create mode 100644 examples/fourklang_instruments/basedrum.4ki create mode 100644 examples/fourklang_instruments/basedrum2.4ki create mode 100644 examples/fourklang_instruments/basedrum3.4ki create mode 100644 examples/fourklang_instruments/basedrum4.4ki create mode 100644 examples/fourklang_instruments/bass.4ki create mode 100644 examples/fourklang_instruments/bass2.4ki create mode 100644 examples/fourklang_instruments/clap.4ki create mode 100644 examples/fourklang_instruments/guitar.4ki create mode 100644 examples/fourklang_instruments/guitar2.4ki create mode 100644 examples/fourklang_instruments/hihat.4ki create mode 100644 examples/fourklang_instruments/hihat2.4ki create mode 100644 examples/fourklang_instruments/pad.4ki create mode 100644 examples/fourklang_instruments/pad2.4ki create mode 100644 examples/fourklang_instruments/piano.4ki create mode 100644 examples/fourklang_instruments/piano2.4ki create mode 100644 examples/fourklang_instruments/rimshot.4ki create mode 100644 examples/fourklang_instruments/snare.4ki create mode 100644 examples/fourklang_instruments/snare2.4ki create mode 100644 examples/fourklang_instruments/snare3.4ki create mode 100644 examples/fourklang_instruments/snare4.4ki create mode 100644 examples/fourklang_instruments/snare5.4ki create mode 100644 examples/fourklang_instruments/snare6.4ki create mode 100644 examples/fourklang_instruments/string.4ki create mode 100644 examples/fourklang_instruments/synth.4ki create mode 100644 examples/fourklang_instruments/synthFlanger.4ki diff --git a/4klang.go b/4klang.go new file mode 100644 index 0000000..7fb7eda --- /dev/null +++ b/4klang.go @@ -0,0 +1,519 @@ +package sointu + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" +) + +func Read4klangPatch(r io.Reader) (patch Patch, err error) { + var versionTag uint32 + var version int + var polyphonyUint32 uint32 + var polyphony int + var instrumentNames [_4KLANG_MAX_INSTRS]string + patch = make(Patch, 0) + if err := binary.Read(r, binary.LittleEndian, &versionTag); err != nil { + return nil, fmt.Errorf("binary.Read: %w", err) + } + var ok bool + if version, ok = _4klangVersionTags[versionTag]; !ok { + return nil, fmt.Errorf("unknown 4klang version tag: %d", versionTag) + } + if err := binary.Read(r, binary.LittleEndian, &polyphonyUint32); err != nil { + return nil, fmt.Errorf("binary.Read: %w", err) + } + polyphony = int(polyphonyUint32) + if polyphony < 1 { + polyphony = 1 + } + for i := range instrumentNames { + instrumentNames[i], err = read4klangName(r) + if err != nil { + return nil, fmt.Errorf("read4klangName: %w", err) + } + } + m := make(_4klangTargetMap) + id := 1 + for instrIndex := 0; instrIndex < _4KLANG_MAX_INSTRS; instrIndex++ { + var units []Unit + if units, err = read4klangUnits(r, version, instrIndex, m, &id); err != nil { + return nil, fmt.Errorf("read4klangUnits: %w", err) + } + if len(units) > 0 { + patch = append(patch, Instrument{Name: instrumentNames[instrIndex], NumVoices: polyphony, Units: units}) + } + } + var units []Unit + if units, err = read4klangUnits(r, version, _4KLANG_MAX_INSTRS, m, &id); err != nil { + return nil, fmt.Errorf("read4klangUnits: %w", err) + } + if len(units) > 0 { + patch = append(patch, Instrument{Name: "Global", NumVoices: 1, Units: units}) + } + for i, instr := range patch { + fix4klangTargets(i, instr, m) + } + return +} + +func Read4klangInstrument(r io.Reader) (instr Instrument, err error) { + var versionTag uint32 + var version int + var name string + if err := binary.Read(r, binary.LittleEndian, &versionTag); err != nil { + return Instrument{}, fmt.Errorf("binary.Read: %w", err) + } + var ok bool + if version, ok = _4klangVersionTags[versionTag]; !ok { + return Instrument{}, fmt.Errorf("unknown 4klang version tag: %d", versionTag) + } + if name, err = read4klangName(r); err != nil { + return Instrument{}, fmt.Errorf("read4klangName: %w", err) + } + var units []Unit + id := 1 + m := make(_4klangTargetMap) + if units, err = read4klangUnits(r, version, 0, m, &id); err != nil { + return Instrument{}, fmt.Errorf("read4klangUnits: %w", err) + } + ret := Instrument{Name: name, NumVoices: 1, Units: units} + fix4klangTargets(0, ret, m) + return ret, nil +} + +type ( + _4klangStackUnit struct { + stack, unit int + } + + _4klangTargetMap map[_4klangStackUnit]int + + _4klangPorts struct { + UnitType string + PortName [8]string + } +) + +const ( + _4KLANG_MAX_INSTRS = 16 + _4KLANG_MAX_UNITS = 64 + _4KLANG_MAX_SLOTS = 16 + _4KLANG_MAX_NAME_LEN = 64 +) + +var ( + _4klangVersionTags map[uint32]int = map[uint32]int{ + 0x31316b34: 11, // 4k11 + 0x32316b34: 12, // 4k12 + 0x33316b34: 13, // 4k13 + 0x34316b34: 14, // 4k14 + } + + _4klangDelays []int = []int{ // these are the numerators, if denominator is 48, fraction of beat time + 4, // 0 = 4.0f * (1.0f/32.0f) * (2.0f/3.0f) + 6, // 1 = 4.0f * (1.0f/32.0f), + 9, // 2 = 4.0f * (1.0f/32.0f) * (3.0f/2.0f), + 8, // 3 = 4.0f * (1.0f/16.0f) * (2.0f/3.0f), + 12, // 4 = 4.0f * (1.0f/16.0f), + 18, // 5 = 4.0f * (1.0f/16.0f) * (3.0f/2.0f), + 16, // 6 = 4.0f * (1.0f/8.0f) * (2.0f/3.0f), + 24, // 7 = 4.0f * (1.0f/8.0f), + 36, // 8 = 4.0f * (1.0f/8.0f) * (3.0f/2.0f), + 32, // 9 = 4.0f * (1.0f/4.0f) * (2.0f/3.0f), + 48, // 10 = 4.0f * (1.0f/4.0f), + 72, // 11 = 4.0f * (1.0f/4.0f) * (3.0f/2.0f), + 64, // 12 = 4.0f * (1.0f/2.0f) * (2.0f/3.0f), + 96, // 13 = 4.0f * (1.0f/2.0f), + 144, // 14 = 4.0f * (1.0f/2.0f) * (3.0f/2.0f), + 128, // 15 = 4.0f * (1.0f) * (2.0f/3.0f), + 192, // 16 = 4.0f * (1.0f), + 288, // 17 = 4.0f * (1.0f) * (3.0f/2.0f), + 256, // 18 = 4.0f * (2.0f) * (2.0f/3.0f), + 384, // 19 = 4.0f * (2.0f), + 576, // 20 = 4.0f * (2.0f) * (3.0f/2.0f), + 72, // 21 = 4.0f * (3.0f/8.0f), + 120, // 22 = 4.0f * (5.0f/8.0f), + 168, // 23 = 4.0f * (7.0f/8.0f), + 216, // 24 = 4.0f * (9.0f/8.0f), + 264, // 25 = 4.0f * (11.0f/8.0f), + 312, // 26 = 4.0f * (13.0f/8.0f), + 360, // 27 = 4.0f * (15.0f/8.0f), + 144, // 28 = 4.0f * (3.0f/4.0f), + 240, // 29 = 4.0f * (5.0f/4.0f), + 336, // 30 = 4.0f * (7.0f/4.0f), + 288, // 31 = 4.0f * (3.0f/2.0f), + 288, // 32 = 4.0f * (3.0f/2.0f), + } + + _4klangUnitPorts []_4klangPorts = []_4klangPorts{ + {"", [8]string{"", "", "", "", "", "", "", ""}}, + {"envelope", [8]string{"", "", "gain", "attack", "decay", "", "release", ""}}, + {"oscillator", [8]string{"", "transpose", "detune", "", "phase", "color", "shape", "gain"}}, + {"filter", [8]string{"", "", "", "", "frequency", "resonance", "", ""}}, + {"envelope", [8]string{"", "", "drive", "frequency", "", "", "", ""}}, + {"delay", [8]string{"pregain", "feedback", "dry", "damp", "", "", "", ""}}, + {"", [8]string{"", "", "", "", "", "", "", ""}}, + {"", [8]string{"", "", "", "", "", "", "", ""}}, + {"pan", [8]string{"panning", "", "", "", "", "", "", ""}}, + {"outaux", [8]string{"auxgain", "outgain", "", "", "", "", "", ""}}, + {"", [8]string{"", "", "", "", "", "", "", ""}}, + {"load", [8]string{"value", "", "", "", "", "", "", ""}}, + } +) + +func read4klangName(r io.Reader) (string, error) { + var name [_4KLANG_MAX_NAME_LEN]byte + if err := binary.Read(r, binary.LittleEndian, &name); err != nil { + return "", fmt.Errorf("binary.Read: %w", err) + } + n := bytes.IndexByte(name[:], 0) + if n == -1 { + n = _4KLANG_MAX_NAME_LEN + } + return string(name[:n]), nil +} + +func read4klangUnits(r io.Reader, version, instrIndex int, m _4klangTargetMap, id *int) (units []Unit, err error) { + numUnits := _4KLANG_MAX_UNITS + if version <= 13 { + numUnits = 32 + } + units = make([]Unit, 0, numUnits) + for unitIndex := 0; unitIndex < numUnits; unitIndex++ { + var u []Unit + if u, err = read4klangUnit(r, version); err != nil { + return nil, fmt.Errorf("read4klangUnit: %w", err) + } + if u == nil { + continue + } + m[_4klangStackUnit{instrIndex, unitIndex}] = *id + for i := range u { + u[i].ID = *id + *id++ + } + units = append(units, u...) + } + return +} + +func read4klangUnit(r io.Reader, version int) ([]Unit, error) { + var unitType byte + if err := binary.Read(r, binary.LittleEndian, &unitType); err != nil { + return nil, fmt.Errorf("binary.Read: %w", err) + } + var vals [15]byte + if err := binary.Read(r, binary.LittleEndian, &vals); err != nil { + return nil, fmt.Errorf("binary.Read: %w", err) + } + if version <= 13 { + // versions <= 13 had 16 unused slots for each unit + if written, err := io.CopyN(io.Discard, r, 16); err != nil || written < 16 { + return nil, fmt.Errorf("io.CopyN: %w", err) + } + } + switch unitType { + case 1: + return read4klangENV(vals, version), nil + case 2: + return read4klangVCO(vals, version), nil + case 3: + return read4klangVCF(vals, version), nil + case 4: + return read4klangDST(vals, version), nil + case 5: + return read4klangDLL(vals, version), nil + case 6: + return read4klangFOP(vals, version), nil + case 7: + return read4klangFST(vals, version), nil + case 8: + return read4klangPAN(vals, version), nil + case 9: + return read4klangOUT(vals, version), nil + case 10: + return read4klangACC(vals, version), nil + case 11: + return read4klangFLD(vals, version), nil + default: + return nil, nil + } +} + +func read4klangENV(vals [15]byte, version int) []Unit { + return []Unit{{ + Type: "envelope", + Parameters: map[string]int{ + "stereo": 0, + "attack": int(vals[0]), + "decay": int(vals[1]), + "sustain": int(vals[2]), + "release": int(vals[3]), + "gain": int(vals[4]), + }, + }} +} + +func read4klangVCO(vals [15]byte, version int) []Unit { + v := vals[:8] + var transpose, detune, phase, color, gate, shape, gain, flags, stereo, typ, lfo int + transpose, v = int(v[0]), v[1:] + detune, v = int(v[0]), v[1:] + phase, v = int(v[0]), v[1:] + if version <= 11 { + gate = 0x55 + } else { + gate, v = int(v[0]), v[1:] + } + color, v = int(v[0]), v[1:] + shape, v = int(v[0]), v[1:] + gain, v = int(v[0]), v[1:] + flags, v = int(v[0]), v[1:] + if flags&0x10 == 0x10 { + lfo = 1 + } + if flags&0x40 == 0x40 { + stereo = 1 + } + switch { + case flags&0x01 == 0x01: // Sine + typ = Sine + if version <= 13 { + color = 128 + } + case flags&0x02 == 0x02: // Trisaw + typ = Trisaw + case flags&0x04 == 0x04: // Pulse + typ = Pulse + case flags&0x08 == 0x08: // Noise is handled differently in sointu + return []Unit{{ + Type: "noise", + Parameters: map[string]int{ + "stereo": stereo, + "shape": shape, + "gain": gain, + }, + }} + case flags&0x20 == 0x20: // Gate + color = gate + } + return []Unit{{ + Type: "oscillator", + Parameters: map[string]int{ + "stereo": stereo, + "transpose": transpose, + "detune": detune, + "phase": phase, + "color": color, + "shape": shape, + "gain": gain, + "type": typ, + "lfo": lfo, + }, + }} +} + +func read4klangVCF(vals [15]byte, version int) []Unit { + flags := vals[2] + var stereo, lowpass, bandpass, highpass, neghighpass int + if flags&0x01 == 0x01 { + lowpass = 1 + } + if flags&0x02 == 0x02 { + highpass = 1 + } + if flags&0x04 == 0x04 { + bandpass = 1 + } + if flags&0x08 == 0x08 { + lowpass = 1 + neghighpass = 1 + } + if flags&0x10 == 0x10 { + stereo = 1 + } + return []Unit{{ + Type: "filter", + Parameters: map[string]int{ + "stereo": stereo, + "frequency": int(vals[0]), + "resonance": int(vals[1]), + "lowpass": lowpass, + "bandpass": bandpass, + "highpass": highpass, + "negbandpass": 0, + "neghighpass": neghighpass, + }}, + } +} + +func read4klangDST(vals [15]byte, version int) []Unit { + return []Unit{ + {Type: "distort", Parameters: map[string]int{"drive": int(vals[0]), "stereo": int(vals[2])}}, + {Type: "hold", Parameters: map[string]int{"holdfreq": int(vals[1]), "stereo": int(vals[2])}}, + } +} + +func read4klangDLL(vals [15]byte, version int) []Unit { + var delaytimes []int + var notetracking int + if vals[11] > 0 { + if vals[10] > 0 { // left reverb + delaytimes = []int{1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618} + } else { // right reverb + delaytimes = []int{1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642} + } + } else { + synctype := vals[9] + switch synctype { + case 0: + delaytimes = []int{int(vals[8]) * 16} + case 1: // relative to BPM + notetracking = 2 + index := vals[8] >> 2 + delaytime := 48 + if int(index) < len(_4klangDelays) { + delaytime = _4klangDelays[index] + } + delaytimes = []int{delaytime} + case 2: // notetracking + notetracking = 1 + delaytimes = []int{10787} + } + } + return []Unit{{ + Type: "delay", + Parameters: map[string]int{ + "stereo": 0, + "pregain": int(vals[0]), + "dry": int(vals[1]), + "feedback": int(vals[2]), + "damp": int(vals[3]), + "notetracking": notetracking, + }, + VarArgs: delaytimes, + }} +} + +func read4klangFOP(vals [15]byte, version int) []Unit { + var t string + var stereo int + switch vals[0] { + case 1: + t, stereo = "pop", 0 + case 2: + t, stereo = "addp", 0 + case 3: + t, stereo = "mulp", 0 + case 4: + t, stereo = "push", 0 + case 5: + t, stereo = "xch", 0 + case 6: + t, stereo = "add", 0 + case 7: + t, stereo = "mul", 0 + case 8: + t, stereo = "addp", 1 + case 9: + return []Unit{{Type: "loadnote", Parameters: map[string]int{"stereo": stereo}}, // 4klang loadnote gives 0..1, sointu gives -1..1 + {Type: "loadval", Parameters: map[string]int{"value": 128, "stereo": stereo}}, + {Type: "addp", Parameters: map[string]int{"stereo": stereo}}, + {Type: "gain", Parameters: map[string]int{"stereo": stereo, "gain": 64}}} + default: + t, stereo = "mulp", 1 + } + return []Unit{{ + Type: t, + Parameters: map[string]int{"stereo": stereo}, + }} +} + +func read4klangFST(vals [15]byte, version int) []Unit { + sendpop := 0 + if vals[1]&0x40 == 0x40 { + sendpop = 1 + } + return []Unit{{ + Type: "send", + Parameters: map[string]int{ + "amount": int(vals[0]), + "sendpop": sendpop, + "dest_stack": int(vals[2]), + "dest_unit": int(vals[3]), + "dest_slot": int(vals[4]), + "dest_id": int(vals[5]), + }}} +} + +func fix4klangTargets(instrIndex int, instr Instrument, m _4klangTargetMap) { + for _, u := range instr.Units { + if u.Type == "send" { + destStack := u.Parameters["dest_stack"] + if destStack == 255 { + destStack = instrIndex + } + fourKlangTarget := _4klangStackUnit{ + destStack, + u.Parameters["dest_unit"]} + u.Parameters["target"] = m[fourKlangTarget] + if u.Parameters["dest_id"] < len(_4klangUnitPorts) && u.Parameters["dest_slot"] < 8 { + if u.Parameters["dest_id"] == 4 && u.Parameters["dest_slot"] == 3 { // distortion is split into 2 units + u.Parameters["target"]++ + u.Parameters["port"] = 0 + } else { + modTarget := _4klangUnitPorts[u.Parameters["dest_id"]] + for i, s := range Ports[modTarget.UnitType] { + if s == modTarget.PortName[u.Parameters["dest_slot"]] { + u.Parameters["port"] = i + break + } + } + } + } + delete(u.Parameters, "dest_stack") + delete(u.Parameters, "dest_unit") + delete(u.Parameters, "dest_slot") + delete(u.Parameters, "dest_id") + } + } +} + +func read4klangPAN(vals [15]byte, version int) []Unit { + return []Unit{{ + Type: "pan", + Parameters: map[string]int{ + "stereo": 0, + "panning": int(vals[0]), + }}} +} + +func read4klangOUT(vals [15]byte, version int) []Unit { + return []Unit{{ + Type: "outaux", + Parameters: map[string]int{ + "stereo": 1, + "outgain": int(vals[0]), + "auxgain": int(vals[1])}, + }} +} + +func read4klangACC(vals [15]byte, version int) []Unit { + c := 0 + if vals[0] != 0 { + c = 2 + } + return []Unit{{ + Type: "in", + Parameters: map[string]int{"stereo": 1, "channel": c}, + }} +} + +func read4klangFLD(vals [15]byte, version int) []Unit { + return []Unit{{ + Type: "loadval", + Parameters: map[string]int{"stereo": 0, "value": int(vals[0])}, + }} +} diff --git a/examples/fourklang/2010_ergon_5.4kp b/examples/fourklang/2010_ergon_5.4kp new file mode 100644 index 0000000000000000000000000000000000000000..f7e900c013ac93b58de073783ed4949096a8250f GIT binary patch literal 18440 zcmeHOQBPAb6h6IocXxMDBLol?u|60fhQtBt^uaU`%s@z%5O~0lY&s%~FvvC^Oib)Q zpS_nZ@X+>z4&uhneHrxhJE!MszxLcRR)3b30|3}QsyE=*=~1i8OX}BaKO3j#-QSSm zm%HuE(Y1W+cH7O?j}G|8U#oXIdi-y%6*FTL?_a3y}pX8~TtFAo@hWkCGufOww;bT$8X9`V`4 z&RNBQ3pNDo2_Yveec}rA?gi;iVt(?>+}>{uHWB}8(7W&OAD5Lq8X*1-5?|%_z!4wI zub-#wGY~312x-dzwQsxed7EQBAk1zXcfUQ{T5-;BKJ54O`;3Gl2w;LOnZp!PtK-r5 zNyg3iL{2%-ci&`Yp--3wAz) z;v2R_Sv*toO)wBi`RAo_#1_nj1Ui53<$s+&tUgG-^P8Y)WPPxQ)mjJQY8)HjZy zWu|au+P?X!9ub!fHG?y@`!~!#wm&3d9&*rLe~S|5w7zMf@^<{bdTn_^Pi-C-c9-JM)~GGR*JuKGPl1*&>XTAXA0 zkM|)g=In=*I^ToHVaLvQb_~)re)awmxZYplW0jcW{RWF~p@o`>fstSU)xQv#{>`VS zy1%nadnEFwL5?;9sy<9t=wr5EUZGfhnJDl$KcxS0p8e6!UF-h|>ECZIN&kMl5o$OX z(E5K$`uCd;(!U>Xgc=S8wEpKv|9Mg z_nQyWzaMXe8V&}u{-2Zn{pN%8@5dXVhJyjEe@^=Mn~$RHi+$Q%^y=X7llu3s!UAKa kzp!H8pNkgd;TAxm@Zf_lB5CE3@gs0Dk2wR$`gjxY4`o%vdjJ3c literal 0 HcmV?d00001 diff --git a/examples/fourklang/LightRythm.4kp b/examples/fourklang/LightRythm.4kp new file mode 100644 index 0000000000000000000000000000000000000000..abe9b0bb4e8e6230aef8b4e2af449ecc5256bbb7 GIT binary patch literal 18440 zcmeHO-;WbF5FXpvY;quUM=6B zD&{{=?NvzjVP|G>Zkv$U4;SB#KYKj(H#2rNC$H|_GXU`5-LscZp1yuw{PyObw|~F; z>-n3v{%QNNE`Bv^@k9GjS1oJ!lhxDqwHp3zd(^7sCE|Zv_1eC6iQ}(5|8aY@mo4l1 z$0$(8-&^+DzEa1>Y`{7`W&_soF&m(c|Ec}7mo3e7h6OGE{pgCtv*LV;WErHzNpW5Z z@&e%;|DYoVpZ@}LYq{P5^7}A%g1jG`oKX6F*A=So1h5ym5e=jk4pG#gHz5fQ0~E!O zl~npBN9G%Ir>SK>YkInR=JDw9*Ug<~H8uRIr{B|jl>cYl@?Yb(X1U&lY+TL1wB-M1rdW&rH_Uh4@L#jf zrsawM2q0|H;Ge-Z?~Sl__L0Zqdp7<%q+V#p6>a}>xDU^5h+c?%{^0eu86V`kjxQhd zOrPCYAj2wu`P>{g?@`Tv4o~+z{!=NXp!qX}|G~gGre^E={O+mG?}Yq3_?K%N%>T4q zX&*iUm_1?rTjhU9INt>SB*@o3e;{l6YHIc*tPkHv8{ZK-^{Rh2kmc+8_i7N~PgYdt zpPS~}<{hZ=ANklI47)QT1Cyd4emePu%zqx)PGSM8{mHF@B`*h_U)uidmE`e!aWvwF zu0RfFV*X-F^y(dsxlT0w9R0~bd8KQTuK86P>XWSM{1=z^lbrL%C~e?Wj{hX{-vEw) z_z`=BAoQ?&3iH0%o!#1DRLE<>X6OB-W&p|wczE-m2WKZyCi!hcOS z?_cEmh1W122>j0{FdqtX%|B@R*&=-ZP_UQu7K41M$Pu|O0og`xw3dioajb;U2c*1zssw;`*U0H|BJ-`xV29FkF#4sZHEOG z{QolXKW;q||KseIP}^aF1^>TF{Eu6Y#Q!+ECDe9UV8Q>d6aVAZ#pK6ADq zPs#pscOHk)u339H(N^mhXgiV%KvyUo5wd3eYqIT2RECx-{;r(;#yyf#%KfZuXveUk7lu+ z4u()}{QkrFcXA3i60^8aC)WPs^er?0{x$zko)&6(Re2cC7nAs(At-KObNsOr1&cCR zEUn~$`Mi%c?h#eO7PQ(mpO6A1@<)ukPOGLi>&YVJibKF5a4QIi{5J*w`A^YM;_t0E z?Tk7EG6Vu+lNowXb@FFu1BF~^3UW?&7i8+Eu6Z$~dlJ5ZC3yMRCR&~`y5BRlMwsw^ z>*{`FtVhYUfxh{J&M&&yzE8Iwp25=hDC_9oGjqt=e*w!Dlb?hUEIr14BInwd@)uD~ zcP&L!T7Ugk)F7ZwU;AHwMT3hqYE#RpgT$|(Wv2!}H6AOK*HArR%|oW&Y#b>lqt9Ch zXikr%DmyJ#(1q?>x{IOZ`A=)EBnqAbSh`j?hGj$1lRQNJHyOFaAG+n0#7oy7ZSv+; zQUljFT%US^{J>Bo^CL2KN*!3)H*HG4PD7LGSC$L=FaPjI)`I}7Z2{b;;&19C^N+NC zG#k{&Fh-r|y%A6?gVC9;F7ZEEKRABZjv3?FY}kWky#l|zpz4T_Vj z5s>)1u1GvIEg^8PKL`Q}49j1PPbT3PMG}!;*4DU=Nb@J#-+iLK$xRxQJCtK?qp3<7(<|Ht@UE&dyJ{Yc__P(8kL`FFBD(fRi@$|rs{gT?$=%8wtk`TSU; zbGXfHU=LVFc7{>g)e$MaIQ~P_v*pK3!4E9|SNq#{BtOOcLEDh?F0kqT6I3UkhC$Cp z2H>Ikz8w4fHs8MFKj*uX-97>$|2sH4_P8J`EVqC6>Z|?r{gN1kKZAFdIbWCfae@~JIBAVa2 z+WngcCSuo*Kk@$0tI+@mlB(-V<^6{yk7iQOeM4@YQhCi-wue&u3?iHCZ$4FDs-rwZ z()miJ-&T0PmsceHJe?x%ny}~he+G-C5%XXb|H=64zW<}aa}tNZmLuT4|J(8ba_+GR zWcNd#QQsd(`ODs)|Gli5|37#6KQlw6^MxJB{~uM%{}2Da{9iMpxBDU&|95|kce#gI z{D17?f4NZ>`H#mVeQj*n^MC1lEQ|m@ug|}`E7zkmmlMgSySBqt2-Za_bU5-Kz-PoSq?h(jYO%73T}^V_#JZ(6R6N%K?EUmGGGn%@$F5A^zP_OJOO literal 0 HcmV?d00001 diff --git a/examples/fourklang/bf-enlighten.4kp b/examples/fourklang/bf-enlighten.4kp new file mode 100644 index 0000000000000000000000000000000000000000..180c886f3cd550a9578613cc27da6478ec2a6920 GIT binary patch literal 18440 zcmeHN-HY2s5TD(ZEh(f$NmJr}q|sY1&<9^~EhdC?bKHYl@*r9aekfE4y@%5DFlhn} zgs}g)J1Z;7J!ocps~Ft7)sw8*`TFhrW>;$+{dM?21Heyb|Gawn`tQ@TSLGr8yzuoA z^&ec=IVwDP{l}lrp1(LPKLm0iGauB#@;~|I_g~MRy*#}S<@XnI+zuY zHug=3xzYnzf07t-MEVfrBU(OIq-rDZ`2iw84b%1npjbI7Ol@bdsI5&1H)dW_rr-Ow zh;K&WYvvQmbfYifH(1Vk2QVcG-6p;f0ZjYY)QwJaYQ1|-XWGLjW-0@vmRkr5%&)N1e#Lw5SqwtgTQwQrjq1c7<*PnK0 zR+oH#Ntdv`rRgFN?Ozw{WBi}$@c6q#h@1ELQZQhDLi}kGw)%8`u>(A5$y8@ZA5EvL3_?BuCKesd!hXo=auK&RI zH#M&+Zv9U!tU=Qys_o-)QPqwrLVa{xE~fPUxZy`#kotkJbZXxI!q$%LKiFS1vwo+K zU~M=#_Me22OZ`RMA5y?}8S*z0KXL!gZrx1H?+=odV*@ab?_bjTJgQx70X|cA)xADP zW8wV*^|1or7PWuQv$D_Zm3gLMQiG$@_4{*<{|e^7ZQ+uVF^+E23f5yq7rDeA{hjCc zG5HcI)jys2b`k}+!DLe0wg2s++}$W?{gFK9bs`Ok@*N_eraXVsw>7sY%dn zC71d`&TlWa@(~AxBjDHX!qNKv{ERz)PdH`TY$wrJAl&((^#4Nolz;yp+#l@YCO&GF z()MRq6xLC4iQgR%#EqHq{liRo0uAjCL|9ut9?hLC0X|pX_P;}BGv3swbgLR5&+vS& z&w{xB-{>pUSAF__2bpgsK8#ZAx3m|0gKJt&&*@@7H$9+hYzVR zX@1P%qdwYnkR(gj-S^$7$~ks^D(mZqJRg?g^Zoyh%KKb5&!qluoMREj`D61pzwz@c z?SII?TR}dhDpS(jP9D$y;`tv}DQ4H_fBf+@wC8`f2cG{;=<~nb@{7ZEA7l!9FSq_{ zzkcEfstHQ{c^`6&FnR5f40RI=>Px# literal 0 HcmV?d00001 diff --git a/examples/fourklang/c0c00n_001.4kp b/examples/fourklang/c0c00n_001.4kp new file mode 100644 index 0000000000000000000000000000000000000000..16f28a305eb985f3d770899887f3c86d3b284f54 GIT binary patch literal 18440 zcmeHOO>fgc5S?{CU2!N-N}znWr9g!cYDz(>#9$yc-oKvX~u5F{?B`_IEV z7-ESXkGtChrygJ8%=qo>KF`}(dy~UY!-EI_j>q37U%!8OKb|B*`}NF+;Q#XX^vvX0 zK!Lx5K>V{n{81o&xbM+GVf;g%1T5lx2rS}#ZeS6A7&v~P8(570+#|kv@^-RqXkdi+ zA^nUdn=4#Lx8<_VZP6oy6#emioVVpV>x>NAOD?ND<-D@naiQk69Dl{)=f=Vrhc8?z zrz^-nY*w(-=h)?H`q$q2uc+UvzWU4cyWxvJM=GVM6=Xp4U#0#}Cf@ZQaWg$RTJ-#J z%~${V{IThaU%`}@E*BVx-xaMz&tz!y&pOUw;nARaYj zpu{j0BpMCjwC0|HDMapP;|!(X6@bak+($bxZbxqIjbPeU)anADONXAd5bfc5irP?@ zar0AY=fue}z|HSm15IClbNnpYuV9qGfR4iJKPlIL-_VU)PwzEMdm;Kq#O(cIpNL}L zU(WpG{6J4VvleCkAc)P)!TL|i+ZXR*8Bp*tDXZUq^h4>3(?`pYkN%4tn6$nW27>jE zOX;QrAsA4g%h&(9eElEcvEAS6Kc?zywtnce=_1Rn@45QjA_kV5TN|31y2^p#t;9gE z{&l-j9Jy#Q45;<}aoHoft^YdJ{`-Fxznfk@Fzum_} g*B&6JuN)s|`+z?93!Z*5yg&xl7<`w>@22bj7Xyye)c^nh literal 0 HcmV?d00001 diff --git a/examples/fourklang/dollop.4kp b/examples/fourklang/dollop.4kp new file mode 100644 index 0000000000000000000000000000000000000000..8070f5ccf071f9fdc73d1e8aa9851ee6a75bc6b5 GIT binary patch literal 18440 zcmeHP&u`N(6n=J_X4wJC7*JpgA2$wY9hJCD!joXy&<;ItD4f!*L8~aGT3ZAMr2OZ_ zZZqwN^88k3V>Gc3t>TyO+3(q({rr-)@vUBG0C3PBj!wtlqy9+L)#cn}+#e64ju^B% zaHeQ39Y^iq5RQ70IPb_@_~TxCpa8o6Z{`9fH|zF0$^kZ5jXCL_v`4mJg*!J}ytPqt ze9|2Zev4kD1|S*8xzu!`(YPOpuif4#I-LtSyZJ)@>suChHF3nY1?~)@@m2$$DA4%E znKFy-i}u^-`T3)lIN#0Q>5}Hat$W7Qh$k=`uwqo28U%~`A;xi7+jL>2Jn z0Aw{@#qj=nm~f7&2Y&}9C4;xFSB z0an;cwmGxt@@K`ne0}vw;iUw4$Tri>pzv4qE!uSeV$r&Mqy1$l0;X(c^E*KesJsRT-K>P>TNy z_5fnMmt|oPDwyAdNriD#0TsXV*D8K6#23{1gEs60*YZGS{>Rs9#@%#F9$+Ai{N?(1 zkY#_5@w0(8iJCv&LHtRrFpt?fOO2zc^J$VFdQ#N*_LiniZqyb%{&4yK+)U*{8j`H< zHJAUl8-BH^hNci*?f+oP?@{qJxxdfy*H2acdLzvOa(|!Yub*M#PvO(rbaln?K%nE_ z0vE;q*mQp9e0siK!CXY~AH&^Ly~@n_-B|6!{Yw(tqW4|V=;7k@s#2NTb*zxqmE@$C0sE_Y=^N>l-t?~L-S z@Au%IaT4tTieHqM0#oowft@_F2iUdvFCG6gdpe&A9RC-4Tpj-xd;IRY_m2N}Jp$R< zcjy0X{&VQN^+0m|UvuYwyKx}-{~2Ig;_v5Y&HtiNDG%246g*{jso2Z6$;wdv~e17+ literal 0 HcmV?d00001 diff --git a/examples/fourklang/example.4kp b/examples/fourklang/example.4kp new file mode 100644 index 0000000000000000000000000000000000000000..9d5ded728f824292ab143c306419ebbfb85c8a5e GIT binary patch literal 18440 zcmeI4yKmG$5Qk@dXZxHkknrX(uLuexE|^z0qKFjXf`Tg%Zm19`Ae2b^pC9YXMRBp? zQFfweeI8fIjDPE$@q9bGl6BS3#bE*f$0vVIfB*gU(0TuhkOJ)LLkh5~4=KQ|KBNG<`j7(b>O%_9K-r+B zUtDfs1&JYVFmMFWqo0;NPB&`)1y-49S0ewF(T`pWl2-^z{A8)yvWb6OwO&mIW+)q{ zurj@>Wf?Glz5FJKxJ0xw;xQaw6KiR>i80kYoD7Kh$Kk}fjWady8K9&i6Zij_r3ZJO zyoOaS>Hp07biRI(`vYcm;?}p`;6~sS^Iwi2NfC!=zPiiO6HKag|Hob8mi)-=-xC_5 z)}J%}S&yIH|5sJDtSWNv)O;NoFwpd$ z|C|2ve}c#E{Ea2mU)%jdI;A0E`QG$*YJ9FgPxcz>=yDzD-TG&U`ERCMpQD%qbmxC} zx~FG&);}AjztVn=PtMQPSK3%afL-&_%zaU0sn2?0bNcU2f5XiED-5T<7)k#cVg?HQ zHpFRP*_@5TpW(ml?8do!T?N5TJHA^-Q3e{XrKk;#Da9|iySh5SEI z{+U8ow>JaIe-!*b6!Je;{=Ma`MkWKwe-!*b67pXv|4gB)+nWL9KMMXI3;BPd{Cmq= zjZ6lV|0wu>D&+r}^3N2yy1f}t{-fakxsd+@<=G63!$q()9=X5Uufc=YaKfZiDJ;^>UelO22ew{3q+Uj3EoGq5; zXQxf!V7kqw{=J$=3;)7c{c9F}&mUzXE&MBE^{-j@*ZwFIY2n`(tAEYHzx79%NDDum z7_)cH#y3d-8{gyt+W003WaDSvvo?V?{-rT{*KB-~1QatVrNX<5w0o{2hDb;b;!1OCU#vBN$j#3o5mk&{Epal z4}-#+mblgh1LLg8s@26v+v!srfjUxn+o^UXLtNyPKJk{>!wfJ3T{9pc%j@?4R6zle zdv7bpF5mz_;j7-%aLuj*6Jle|IzEo-c!e zVErq38N=j05C+8OvLH@vjt*Dv|K&Ckk{&=2NB9micwYW)cY ztmeb(7jj`e&B6MwdwJwJ1`cb^fY8s+wfn#P_x%oie#SoytOiBpM=X`@0j>%O`se>b zeLt)4*EHT)D7+KI#*JZMEH`GV&WZL!|JMWce}nb!yu55A7-0SPV*kGxsQ+86e~=D+ z>H6Qo4)#}U#S`^M>c9Evb3D>@28cReQ{l(uIiMzZ=S5>9Gf*+GMKF;N`Mv4-{|g$U B%sc=9 literal 0 HcmV?d00001 diff --git a/examples/fourklang/kevinspacy.4kp b/examples/fourklang/kevinspacy.4kp new file mode 100644 index 0000000000000000000000000000000000000000..2b3372a0e5967a245251e11d837a9458f2a31265 GIT binary patch literal 18440 zcmeI3-EY${5Wvq#+q8RPV~jBd3*X%f>c&7I4e3A#6^VzwRN^t9O&VfM6KoPMNdC`@ zn^aAh?7JvV)vk7)(mS8+yZGnx$9C{zZ$AKlcb7-szkWMCjz69KzW8|f^6@^7~BFSP%re5-lMg8g2v!uToLSNQ-% z`zjxxXkTRj73~l5u#|o)+E*C>Mf+E!Tg?lKev*8V`R}W55yA)paaBD19pGU+9H4AW zCI7z!*pG(^%JE#Idx98Yh>2X)DU~ex0HYu~V#{20rVkArn~1|L z*``W_h-42MCz3LUHj-*+CNfb)R>h9k)s<{Db<$j>>`~dTiCuj+Is3e(>-pA!_@zX_v^|8pY*!Qfo+e?zVt{9*Y&oHPG}O+7dhf5P6TDKu$`21*0;AU*SDLlu5V{GZkDB9*Vi8c`p}o=ugT?qvt_0c@S2=o zqEi^PiToe+Vbme?-1>m)Uv~cxo6jfJ{&0u`MvulrfbfLg->~-&=A}{Un(g`~+=!VL zxCqjEZhm*2KRSA|*Q(8TLM_o%{s-!Qj~jp7_n+Kf-BIV)U7few{nOmFJ($|JnW1U4ie!1evd zg7AOOlK=aje`k=b)ltv?LgfD)Oa5cef2~+JQ<>*~A^3k_$^S#ozca|z>Zs>`A^3k} z$^T=|f2~+JQ<>*~A^3k{$^TQ&zca|z>Zs>`A^3l0$$!`LUn>^QROUMWE98H#0W`bv qF9Ha4kEi_`IPQLJV&56x1M2P&IeTXYqIa}X1XdWU7BjwG=l>rUU-W|j literal 0 HcmV?d00001 diff --git a/examples/fourklang/punqtured-sundowner.4kp b/examples/fourklang/punqtured-sundowner.4kp new file mode 100644 index 0000000000000000000000000000000000000000..e2185410f1a319bf529b9a8d576a254e543c017a GIT binary patch literal 18440 zcmeHOQE%He5I#zlWfdKkbZOUZ&B}*jHHH;%(qt(5#2H1Ab$JkA7~9)yG)5CNa{^mI zv0_E|&plF-J3sJvHU+iLOg+Q`kKd8}$nSU(m4ml?PZnrvn+ci-@#h*w>y*zfXmL)>9QKvu%t2SlL@u@(Zp1pDg)}IEMaxAuyTe@#D|>;sZka$Kqz=~$*zp)KWpBcr4i;MT8ey7R?NolI;*VDFUG~Tvf2jN|5nD#yHl>hli+`R- ze6%|M2c>^Ay8zVu;GVt3Gf{(s^RT*GQi+B0FJj%S^q7G zJ6D$Y7-n%5{R<)V^9hzjs{AFlV-bvcKKJ>Jb^4&P{<-z_hrMxct;_&}S76)!863fp z#GBFafm!o|XV&~6K3C807X9n@3yH-}603W*Du9Ty>OPH_je8R z+R)5sdu%zMi`!Mt?~3mSupR^=P5UW+J{O2ir{BTtRrB*u{((`KFP!kD-A=_vYJHdI zWB1u1du&>%)cIeD|Asv_4}-#++FYlyWCvS0>(tBo`r`G!5&Hj@9aPf)cV7SXY4CG; zX6pU8uyjE0|DfKlo1?!!|F=-*O`m{)GCz6XeEzSN_a)&lYy#C#I^QiS;pw$0EuYb4s@cMV-&7;)&lY yZV_MSL~Z#&=4Zq_5bY!K2dluy+fqVk8TmeY7+e?;NHC93~c)MNB;sg2J`X& literal 0 HcmV?d00001 diff --git a/examples/fourklang/untitled2.4kp b/examples/fourklang/untitled2.4kp new file mode 100644 index 0000000000000000000000000000000000000000..e8c5d6dce5759828a8c412b73b30acb269943de4 GIT binary patch literal 18440 zcmeI3+i%k_6o-$KHfhJxxQsDi3s-FqNOTMY0>l9!E&`#x^t~V|fq*^0#v79V^We1A z9ER;<)hPsg{N(!hXZxJw^Ce05J`Hvg0C;@({^Q$spC`r3{ci_f55G+I4^*jt3f1dw zi(CG^>Y5Ge2f9`NT&@3If0eFT(}((hSGVe)tMz}+U!`l-^n;ta*gIF(FLD9u`b924 zUBAc$sO$Hdxl+|2gO360D2vM$@jH)bu3T2Ym)XM%4%G6=c>_eaSnAw}2*;C8F=EdoT?73>B zh|UpoK1i1wdgwGmcPz)M%Bt8BJEodh*Ho&@lzyo6C&i9=n3P_(^l#?+puCSbi2{p4 z+5?b(_v7D&QS|&z)@@VcH0TZSe@biy@qap6{t@jre*_jWlyWHB>uA0P;zWm7gdM}D z)_;eTO&o#$J^}(>0TgyOb4jiLh}Hhxwh7J%bkj%f{jq&yY-vLTsPhRm#y(Ot*uKmN z2tai@xA#EtFW?z~L*H>7E>ySq5Xwy0P7eUo`kmaUo*z81K3|~bAG_yl@rgh%|I()y zuCuHNDE^)NCK$NBODmeoa*5mhgAoYkU)_G3{$2zGJP+Fclk0y^I-Sd3Lz#!H|FiiC zZRd~V{%rm-KiyXQ3;Wh%I&R)SI4>Xhe!{H=;Qc=pq2=0z;>WfBSL^@u{hXB_;(Sl7 z4_%b12RI|J+KBv~sP|8E{hHDn6QwtbxGz9#Uh%FwvKAIbm9bG5_D}*W|?(f+7{|O8$ B+Ux)T literal 0 HcmV?d00001 diff --git a/examples/fourklang/virgill - 4klang basics.4kp b/examples/fourklang/virgill - 4klang basics.4kp new file mode 100644 index 0000000000000000000000000000000000000000..2f02041ef304dd9b1f2a95e7f4e995942244aa74 GIT binary patch literal 18440 zcmeI3OKaOe6ov1|vFv2kG-=ut3Zf8DcXb|Zvzr96C@pNwDk|B=mfLI!`sbAO}vAdF4 zEbH$QdpS*`WE>BI!SGiwj?&v7K{D#k@|{GZq16Ut(Z9_Eq3tHb`gc*B4kpVkp6^iT z$G;u4^8!B(r|D#t42Eg&-l||Jsi6NLg{+1J{kfGuN-F4kZ>5;cu&D3Z2&9~%{#c6H z42${`8-bKl)SpT*n_*FZW+RYtiu!j_%x1{wEAdR7>2XfPdFClnyeoa&R|ysOTXx!S zEvrC&lN{)BeYM+$xL&>fQAXdZ@qe8gs5u{2&F8A`zy2L=b_L*H-+$ZZ|NOH;+4aBq zW5l)RSLc#E)>6L-@l}PD6^psGa{&D7TgK?KmLHGv#q(W-i78(7e5k6|S5L&QuC?`d zE~w?<{4m*A+c@~ww}#O-?d$)M?=8h`T6<4aN5ev~a(?Lx*L{D{`9u%imikKqJpUDq z-H>VUuWw`yua+Mp|3`=FH`n>nRr&Xh_+p24CM?TL9k*;~Fw;=KobgoG34o z@%+llWAoWZ{#6h8s(h53H>m32fZ+cS{JZr7 z{JZ%ERXrRK{J#YMZvEKl>UHff`@{3U$#g@;%6?(xx2^Qsxt?6~JNny`Z`i*r JX!`&E{{o~)yy^e| literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/BA_Dark.4ki b/examples/fourklang_instruments/BA_Dark.4ki new file mode 100644 index 0000000000000000000000000000000000000000..882c2ca98a66865454b6a502b642b48ec6a7fd43 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ80uu7)Sw(3=R$s4Y+kMIXEzc0#&f!P{L#ak!Qpq&z`{W zpOuvfhb#jF8!nAd9ZVV^^XnUgaG1cxj6(t{%xqwQ*9JoRIUMkt&&tpM3?T+VMh6D0 dD%n_x(k}$mj};Kok5$vCAkiVf35vZD3;-V(6 f*jTZP!$iUQ^&Es4vtS~lG(AIrQ-Pkg4L-X7AgUHd literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/BA_Deepness.4ki b/examples/fourklang_instruments/BA_Deepness.4ki new file mode 100644 index 0000000000000000000000000000000000000000..15f3842ffabf40e4dbea029fd28d5bceb18cc4f8 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~m7128fu6gV{C*2v`Gz!2)-P|Aoy33~yN5-9I^}y zAbECHJn|Y0|2go;v*EHBY9rKr4Y+JzatH_r4RF9CZ(tB=fLopom%&i`3CS}XMBw!S zA$c}toIwkg;Bde%&+5?N08}E#sK%&{O(h#EHc^BaRKF0TI(Bam(vL7{aI!cV5(cNk H=;SH@oIw~N literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/BA_DirectPunchMS.4ki b/examples/fourklang_instruments/BA_DirectPunchMS.4ki new file mode 100644 index 0000000000000000000000000000000000000000..28657eb3025a0f03333ebe942a742049b4d8e348 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f;IQTT+*2rGK@SlN+5x0T?mSHk+UgUfLV+&8Wdf6ll|rZj9{C76gHQtn1zgs!;W8NNEJE_k4i31C2AjZA(Ev3GJH^I~ zT^uIL;Q(V{r&$vc9Dqs$8TlCau&H2U#U_dngX$Lo>cBp*R7zuKMLU#lM04Ws{ AvH$=8 literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/BA_Mighty_Feedback.4ki b/examples/fourklang_instruments/BA_Mighty_Feedback.4ki new file mode 100644 index 0000000000000000000000000000000000000000..d716e68498a8ea6d555e010bf77e0b806a2f5244 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~m7128f;ICwSS*2u^ZA>e>Vo;`u#KRY`UP6OB#82)py zFyoYGW5g*1mSA#lVF-0_XuxFylZll=r~)4O2s?vN0|fNs5n@pNLO}gk0U`ZZH4Py_PEhEL GU;qHGM-@c? literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/BA_NotFromThisWorld.4ki b/examples/fourklang_instruments/BA_NotFromThisWorld.4ki new file mode 100644 index 0000000000000000000000000000000000000000..8c8d8204d42b3fa8624622bbd8f5b0dd057177d0 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f~G_W<`*2pC25E1I&&>)CI3A+cwe;yWQ9I^}y z>;??~`S8fIG2+w;mSECwW(c)#XkfyjjJ<&2KRXL9Sq26s7hi@@@tOu))-XAEFoc@m zmS@B5HL!~a%Cq2bI8>Nf!2z!dHdX@q6a4Y(#~n0agPDa@@v0!?KMn`{?qhXmZ~!V1 kWRzu;#io*w|DgJXfcmj%XCR~>t0p8t&V~jg-pFGD0I_@+`~Uy| literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/BA_NotFromThisWorld2.4ki b/examples/fourklang_instruments/BA_NotFromThisWorld2.4ki new file mode 100644 index 0000000000000000000000000000000000000000..382879ac7ccfb7aa76c50d9245cb3010c82b0c41 GIT binary patch literal 1092 zcmds!Jr9B~5QeWuDd?h|T!`T(i0Qz11A`8ZO&D32jKnDJ{pU!CZr+jT=B>}YAHBMB zu|IbA{`R=EU7r{5#VDwQ3A_;3RC}UenU$o1VWlI60vN@j37{nq{dX|fAN=KX0L&`z zWgfVi$rXmBM%D()6Zj1U&1|4MfOkAM{OKmgS^Xd*FS d?tP+ogfWF?Oi(G>?WaFXM<~sL8VUbBdjcNw7(f63 literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/BA_SawBass.4ki b/examples/fourklang_instruments/BA_SawBass.4ki new file mode 100644 index 0000000000000000000000000000000000000000..2a17c29210832018458b92fba16bc253e644a240 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f;BsesXp%JQuk-@_OpMLfXhW|V)%s4GzW5g*% zlmwH53qvS_Ljw~I)l3dH_~m^Z7(&@_>u1AdFw{Om@;ICY7iLyypeO{`a0e~i4txw& zhXw}+1_n+J4Mqlv9D>6V4xr)q0MLE~pb|kwCG57dvEtLszyQ@R#Hfm0KYrank2erf HF}&pg3sMwq literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/BA_SawBassFlanger.4ki b/examples/fourklang_instruments/BA_SawBassFlanger.4ki new file mode 100644 index 0000000000000000000000000000000000000000..e449bbd8a36366166c41ebd569129738d9712646 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f;BsesXp%JQuk-@_OpMLfXhW|V)%s4GzW5g*% zlmwH53qvS_Ljw~I)l3dH_~m^Z7(&@_>u1AdFw{Om@;ICY7iLyypeO{`a0e~i4txyO s1c3wr1#dw{0eq@xC&%j0;NZZ(AjGJFJs3EElC%Vz4GOe0e$bl@0Cr^)Jpcdz literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/GA_RestInPeaceMS.4ki b/examples/fourklang_instruments/GA_RestInPeaceMS.4ki new file mode 100644 index 0000000000000000000000000000000000000000..cd3cb676ff8b392eb959bf5ea7a15a28ae742db5 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6127sm1OzmYp%JQuks-n%0*`+71cv{7tV}p90Lk+c zkQcxs&&G(;Xs`s6hXX^XgF}NL4rS~XK=(1>ai1^4e*rx51noBbwqbig5jp>l8v004MyAU6O2 literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/KY_GarageOrgan.4ki b/examples/fourklang_instruments/KY_GarageOrgan.4ki new file mode 100644 index 0000000000000000000000000000000000000000..da129ca67d4f256d0d0542584b8f64cfacc2f1a0 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f;BsesXp%JQu$-%)PRKTHu35O9(1weTR0Rcu_ z@)00;1qEF4%qb1Hq;QxxxP;ko1q{@ggybpp4I4A1CJb%;91i&1&&kk$U%@b!1pp@U B4gmlF literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/KY_GarageOrganChorus.4ki b/examples/fourklang_instruments/KY_GarageOrganChorus.4ki new file mode 100644 index 0000000000000000000000000000000000000000..a35e405514d4870e38de22149d165db7d1fac3bd GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f;BsesXp%JQu$-%)PRKTHu35O9(1weTR0Rcu_ z@)00;1qEF4%qb1Hq;QxxxP;ko1q{@ggybpp4I49l6Ig-q<6z(*$XI}1-B6X~aKP_! MP6Y-03Wm8X04zTb=l}o! literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/KY_Lullaby.4ki b/examples/fourklang_instruments/KY_Lullaby.4ki new file mode 100644 index 0000000000000000000000000000000000000000..ca0eb2ed6aed089b4e1be7f2a2889f3fb73856b3 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~m7128HCI5;%m*2om$zz_;l!H7c%8#4|Gs4#~EetA|0 sAYfn+WaMPv!lsgq6`LqR460v<5vy(n215D~CXKR2VhC`8Qr`#$0DqqhE&u=k literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/KY_Lullaby2.4ki b/examples/fourklang_instruments/KY_Lullaby2.4ki new file mode 100644 index 0000000000000000000000000000000000000000..08712451d91859dfd28b51b6d35c83ca5787cada GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~m7128HCI5;%m*2om$zz_;l!H7c%8#4|Gs4#~EetFge y2M3@MK}Kyx8Eh)qSh0yB#DMw}fck|PWw5)DkbZoFaC^KMGH$@=;0Lcvv ARsaA1 literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/LD_AlphaOmegaMS.4ki b/examples/fourklang_instruments/LD_AlphaOmegaMS.4ki new file mode 100644 index 0000000000000000000000000000000000000000..d46a643682eef8376344db8e88b84c9d300f90ee GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f;DEKsxp%JQu$;5#n)WM-a5Qh=$1`Pi>n3!UwOKN~IysOt#H6Y>Nj zLx7w^18!C9!0;1dVaBO|((q$;Xuul-Y`Cq)=_X_eR)+>)2r~#WDq**V11O0Pur`2Y Ug%}mUGzP)xfI|X9&MLLrlP80Jd!MRfeYm6&Zhjz`wz}5Oz?@h+NSi`sBg7+OdE5r_y3{s;H?I6zw_S3Vq h9TooOb57)jKdp~K0uK=7(PI42YzL*9Ad~Rlvls4}7_tBW literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/LD_RestInPeaceMS.4ki b/examples/fourklang_instruments/LD_RestInPeaceMS.4ki new file mode 100644 index 0000000000000000000000000000000000000000..7987ceb7db5b59c2f89bd630500431decac7e73a GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6127sm1OzmYp%JQuks-n%0*`+71cv{7tV}p90Lk+c zkQcxs&&G(;Xs`s6hXX^XgF}NL4rS~XK=(1>ai1^4e*rx51noBU=U<9#cm7-P!b~G7? z{@U!0-F@gEw@WwJ2EKR#T4;$c6yU%mxbYC`vS0;9zDaN`*wP4endb6YyRuR3n?VD d0F|(x+bnQ8B;yoCPzxLV%q+JIm}UNF;0X=S8!-R? literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/PA_Fairies.4ki b/examples/fourklang_instruments/PA_Fairies.4ki new file mode 100644 index 0000000000000000000000000000000000000000..aa4416c7d82aa247901934db992bfa52408e7ab3 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~m7126_81Vl98*2u_^pn*@GMFEdypt0-;4F5SlSU?s6BPO*7ytkl<`XCY literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/PA_Jarresque.4ki b/examples/fourklang_instruments/PA_Jarresque.4ki new file mode 100644 index 0000000000000000000000000000000000000000..a1be725a3acd813ff42cb835a0b971314f80d8b1 GIT binary patch literal 1092 zcmd^-Ef2yl5QcC4S_!61qOdqd5CnA+62weB20;iDF#P@VQ3jH`CIrVT`}BD)ZEkh0 zm%H}XUGJA;+s$hDB1J>g5HG5L5D}?ju9OY*!Dxm8lndy`65k|ldEXUm^-qApHAI7? zm^QWFLi7Sh#PmPGGrm4mhC#!z;|9N_EEn6Q$uFj-BsMrJcj!aL&%94=2XOpq45~#! S1eg@_&x_&{lrnnSf1C%>-WLG? literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/PA_JarresqueChorus.4ki b/examples/fourklang_instruments/PA_JarresqueChorus.4ki new file mode 100644 index 0000000000000000000000000000000000000000..200d74a4dad2c760c27c3b5f198e0c1d3c54ec34 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6126_OGz2u@*2pB_z!2)-&>)0E33~v;e*qR|9I^}y z>;??~x$((c0p(e7>t|!cX*5+Nm>et^LRlOdm~fcJ}rvm{6FonaE1^|>07oPwC literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/PA_LoFiChoir.4ki b/examples/fourklang_instruments/PA_LoFiChoir.4ki new file mode 100644 index 0000000000000000000000000000000000000000..8cc7e241a88e376a821839acc8c3fe0a3b3ae003 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j612DQdI0Q71p%JQu$w7x9l)<5a35O9(4qgnQ3b^Ij zaB0M09=b3~K?AxF3ZL0jo)JX=jl*oDfY${q4o+w~sLN$zrmlG-%`{er1_uWQ20=zC T?9t2tl*9)(9pv#z4|_QPM`I4i literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/PA_LongPad.4ki b/examples/fourklang_instruments/PA_LongPad.4ki new file mode 100644 index 0000000000000000000000000000000000000000..b0736995db001577745b48178c89737a58e2dd93 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j612B3eBqTK8*2tKUk&)nlTb{jv;Xf}6Gfo9;j5wvh z5==4<451DV4MI4Su_rM6=b^-WhX96928RYF9Og4QSb*)vDQ^OFp8{_EY`8Q+?IR@5 zY}$Z3@PKA83mD)HK{mX)85lSmaCjUl%<9nK08}E#sKqFYO(h#EHc^BaP=5kYzYwD= Tc4rdOk1%OqSezi=k6-`*&EXh| literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/PA_Minorium.4ki b/examples/fourklang_instruments/PA_Minorium.4ki new file mode 100644 index 0000000000000000000000000000000000000000..1a95f09ab6cd33e91d14b62e80df6d686b5435db GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f;L>M&S*2pB_z!2)-&>)CI3A+Kqe=Zhg9I^}y zY>YUiz!FRx1k6`p_|JpKe0CRx|J+PWI4xn1VEE66M?XRLIRo7%;NXGF24;r_ygp=d z2kUnrTfYNXe*(Gs*>Guuh8Q7v#)yc7ga(|!%K!>r5emYWSwR4I;sILAhF6w>fz==Z Ylzaslb+AVRFnQqxoDB(h<%hc@04hWr#sB~S literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/PA_Strangeland.4ki b/examples/fourklang_instruments/PA_Strangeland.4ki new file mode 100644 index 0000000000000000000000000000000000000000..b9339c45c92891c369610d0bdeda39e9d49a98fd GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128H?IK(vI*2tvhz!2)-&>)CI3A+cwe|{Eb9I^}y zY>YUiz!FRf4i1FO7o@;^{I)YNFgXDI$K=q!f)CI3A+cwe|{Eb9I^}y zY>YUiz!FRf4i1FO7o@;^{I)YNFgXDI$K=q!f)CI3A+cwe;y_#9I^}y zY>YUiz!FTJ1kAT!_|J>Se0GpLD+TuZ60jfWKR!J66Y?K^-!m{UIRNcvaA?5g14`xD za2X7B7$JE=?q|+Oz#GGC%!Ew9FVE@#3?K#uK}H?y)^LDi@c`Bau%r;90+_}iI0YOq I#72?;0Naroh5!Hn literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/SY_RandomArp.4ki b/examples/fourklang_instruments/SY_RandomArp.4ki new file mode 100644 index 0000000000000000000000000000000000000000..e5a8515559c31fe2774d5b06727e69d4e94e860c GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128gpFbFi@*2o^g@Sm538K(jke426ULP#)iI531d zI5cn|giu&44k%m<4!ak_e?C0!V`Ie7LXrTJgB{p?EI4#AIe0OI3OF?2l4oPWp^->o wW`hR2K43O*z$?#&JL-sZ!QfWL>d@fez`!8LsE$1tfXNFl;A{}UD?i*N0dijw>Hq)$ literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/SY_RandomArpFlanger.4ki b/examples/fourklang_instruments/SY_RandomArpFlanger.4ki new file mode 100644 index 0000000000000000000000000000000000000000..138fd02246849c443a94a36ebf360a2736b459b1 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128gpxC%7j*2o^g@Sm538K(jke426ULP#)iI531d zI5cn|giu&44k%m<4!ak_e?C0!V`Ie7LXrTJgB{p?EI4#AIe0OI3OF?2l4oPWp^->o zW`hR2K43O*z$?#&JL-sZ!N4kGEf6RWP;d}r6u|BdR)+=$2L=WqMs@7L08CzZ0cQga KgYoJbK9T^NM-;aJ literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/airy.4ki b/examples/fourklang_instruments/airy.4ki new file mode 100644 index 0000000000000000000000000000000000000000..e409315b93d83653f6e880b0f47a93ba89241eb0 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6127r{I5;$rp%JQu$-#jk)WN}l1BVeT4mc!W`USxH z8-#EuV`IW00TpJ~VEE6&!i-Z9yA@EL6}LPaBTlVENian~+>gr`VE8xy!-o}D_%QoQ zu;813N}{!HZUg;&`%)#@S6^FKkldlYh`t40H#0&K}JpN QF~9+o#0NM*x<@bo0C%SqTmS$7 literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/basedrum.4ki b/examples/fourklang_instruments/basedrum.4ki new file mode 100644 index 0000000000000000000000000000000000000000..48e2431e36c42022e91d2fc26b69a780444b458c GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128fuBsetS*2vz#@SlN+5vKx11~&! B4aooi literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/basedrum2.4ki b/examples/fourklang_instruments/basedrum2.4ki new file mode 100644 index 0000000000000000000000000000000000000000..37258513c5b958c426cdbe6e09fbdbf5b085c46d GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f;BsetS*2vz#@SlN+5vKx11~&!6*?U~+I!2n}#( zV8Nk+$-zS*G{K>P375Qr14F1oRD&Q6d3G0u|4dA{bTcrp+c5lR#v{*$+v#8%*>D>T zk!Qx?FsLxIkOK!k70e0-cwN8}(ST1o1BU}%X$Dq@h6JDzK}H!yO>8RJSh0yB#DMx8 Yfck|PHL*LBkbZoZD^=709wGP!GzTsg7KHo08C;72RIz?+s)}9fM3BdmjwVA C>kZZb literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/guitar2.4ki b/examples/fourklang_instruments/guitar2.4ki new file mode 100644 index 0000000000000000000000000000000000000000..407bec704305fa215824da7b2b014e25414c5b97 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128f$G8sNj$Z0S*WJPUUovz^`DK%K`w- CSq*jo literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/hihat.4ki b/examples/fourklang_instruments/hihat.4ki new file mode 100644 index 0000000000000000000000000000000000000000..4ff41108119f55d59f2d1e96a7c6cd23036ad351 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~d4128f;FfcUW*2v`G;1KHI(7=I12^%vG38*l0Ljx0j X6{E64IRrQyD04k0h59M8eN;C9c9{wd literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/hihat2.4ki b/examples/fourklang_instruments/hihat2.4ki new file mode 100644 index 0000000000000000000000000000000000000000..d524356c9be083494e91e2135d1365a010210714 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~d4128f;FfcUW*2v`G;1KHI(7=I12^%vG38*l0Ljwyw z6&w!uWEmJ(9U2mVN(32Y7&Wn}WMjo9iVy?pcL3@aV$@_r2qLiv=|@sJ${dCv!09jy IJw<|-0Hq}jWB>pF literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/pad.4ki b/examples/fourklang_instruments/pad.4ki new file mode 100644 index 0000000000000000000000000000000000000000..e0c5f885525092afd23961504dff09de5bca0b31 GIT binary patch literal 1092 zcmdUpJqyAx5QZ;FzZ~o6V%Cm|qrwReb#p0*Qxpq|+Ckg>{&@{5bjd{omu3q0zR%q= zyG*9b=6K#8uBBdAkNV!+%BqH6p2&^a5>LbkEl9x@B+hlQK?<0L#9S8!NpiVHC~!w9 zlgBG)rQ~RUcmb{QsS zhXy7bDwrHR6hadU9B`Sy6i~nr>LAb{f zjRvb=W5(eys4#PY1YR4M4J7c|z~O-3eAa}91fUW@MtMd}Y%1AUv56wYfchPP`h^%Z Wu{)EHeuPQGlf}uPFg!g*q^|(Ga2X%~ literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/piano.4ki b/examples/fourklang_instruments/piano.4ki new file mode 100644 index 0000000000000000000000000000000000000000..d2ec64cbf7e1b65d2a39633e060b953e6a112a34 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~g5128gpF?coL*2v`G;1KGN(7=d82_bnlW*i!!!i41U a>mHRI=n&v=pv(oF4zZLOFshrP5C8yop9~iO literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/piano2.4ki b/examples/fourklang_instruments/piano2.4ki new file mode 100644 index 0000000000000000000000000000000000000000..9b9f28df78219ac7bdc0f621b9da1e6ab53dab80 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128feG&nTi*2rGM@SlN+5vKx11`7rTJo=d&92i0! z92z)qn83!2Ljo$yY|wz)a{O9HWvLJXtPKtIptxhyV8Ut|q4*&bzgP{Wq9BI@e#1B& K1n?^u=CS}9G7jYc literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/rimshot.4ki b/examples/fourklang_instruments/rimshot.4ki new file mode 100644 index 0000000000000000000000000000000000000000..e4ca03d58fb84eb3d6c7b39cd77b639b4d406178 GIT binary patch literal 1092 zcmXrXHZ*bxiVt+_%w-@4FfxQPcr@VF$=<;5pMi-Hw*mq(j0_eG4EW5?VffF% z$b{1rb`OUCoOtBf$afmpP9_HjhEN9u2VBNLz0RS%84)Xv2 literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/snare2.4ki b/examples/fourklang_instruments/snare2.4ki new file mode 100644 index 0000000000000000000000000000000000000000..a4669a5cb36d06cd68d862606a7ad3bf12ef45af GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~m7128gpFnBcJ*2vz#@SlN+5w`*YGK>rs3=H_p&tdq_ z!N`Qu6m}1W|D1T_*~oVq*iI$|2Zm4w1qWQlFgbwa0|>}FIB?+b0dqzJE~CLZ*l>HD mIC*9q785DVET%xf1wKP!rs3=9pp6|v_q z{O4d~!l{7WgW*3X9(gwMod&j(Nx^|3)Iq@kmoZEZAo&0S@>~uaIDEjI(16Qmi2Y#m u9RzUcXTu#dVEq)!GvjbLRG3*zfq)C}>n9}3;ecNSCjoirk3=9pp6|v_q z{O4d~!l{7WgW*3X9(gwMod&j($=-n>)Iq_41BVNk96<634!B&%R1IfBf>Jva}5W0P(&NivR!s literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/string.4ki b/examples/fourklang_instruments/string.4ki new file mode 100644 index 0000000000000000000000000000000000000000..c6ab016702f14b59c92572fe904f4b6c46460454 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~s9129H_0B(&;J`N6{4h{~2IFzuvG5lv`V!|QIzyOkG zW5q4c#)wlZSc1vH(IM2t!2y>EOb+&V<=Jp)gj&nSjNkrIS^Ocu;ecPBkSr%d10f}& I@-z$q09jTLaR2}S literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/synth.4ki b/examples/fourklang_instruments/synth.4ki new file mode 100644 index 0000000000000000000000000000000000000000..2fa6ec14dcc203c3b1a57d5d76c3257cd11143b6 GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128%OK?81$Ob!kVp+FT(IFzs_F#Km>!6nPUz{ZF} zBUG5l0Ajun4rT0t4FB0FF<${>K1YKf4)bX(%&efmPKh0C%#`RJ{(4y*8h|O3L6A`v RdqU#?O5y{YAl)Mv0066~5z7Do literal 0 HcmV?d00001 diff --git a/examples/fourklang_instruments/synthFlanger.4ki b/examples/fourklang_instruments/synthFlanger.4ki new file mode 100644 index 0000000000000000000000000000000000000000..452b9586ce8190cdf30e2baf16699523d4ec019c GIT binary patch literal 1092 zcmXrXHZ=0gD=sN2%}vcKQ7~j6128%OK?81$Ob!kVp+FT(IFzs_F#Km>!6nPUz{ZF} zBUG5l0Ajun4rT0t4FB0FF<${>K1YKf4)bX(%&efmPKh0C%#`RJ{(4yp1PTNc3>= 1 + k++ + } + text := "" + switch v { + case 1: + if k <= 7 { + text = fmt.Sprintf(" (1/%d triplet)", 1<<(7-k)) + } + case 3: + if k <= 6 { + text = fmt.Sprintf(" (1/%d)", 1<<(6-k)) + } + break + case 9: + if k <= 5 { + text = fmt.Sprintf(" (1/%d dotted)", 1<<(5-k)) + } + } + text = fmt.Sprintf("%v / %.3f beats%s", val, float32(val)/48.0, text) + return Parameter{Type: IntegerParameter, Min: 1, Max: 576, Name: "delaytime", Hint: text, Value: val, LargeStep: 16}, nil } - return Parameter{Type: IntegerParameter, Min: 1, Max: 65535, Name: "delaytime", Hint: text, Value: val, LargeStep: 256}, nil + } } return Parameter{}, errors.New("invalid parameter") @@ -1240,7 +1269,7 @@ func (m *Model) notifyScoreChange() { func (m *Model) notifySamplesPerRowChange() { select { - case m.modelMessages <- ModelSamplesPerRowChangedMessage{m.song.SamplesPerRow()}: + case m.modelMessages <- ModelSamplesPerRowChangedMessage{m.song.BPM, m.song.RowsPerBeat}: default: } } diff --git a/tracker/player.go b/tracker/player.go index e1b9095..4bb4b18 100644 --- a/tracker/player.go +++ b/tracker/player.go @@ -20,6 +20,7 @@ type ( position SongRow samplesSinceEvent []int samplesPerRow int + bpm int volume Volume voiceStates [vm.MAX_VOICES]float32 @@ -242,7 +243,9 @@ loop: } } case ModelSamplesPerRowChangedMessage: - p.samplesPerRow = m.int + p.samplesPerRow = 44100 * 60 / (m.BPM * m.RowsPerBeat) + p.bpm = m.BPM + p.compileOrUpdateSynth() case ModelPlayFromPositionMessage: p.playing = true p.position = m.SongRow @@ -297,7 +300,7 @@ loop: func (p *Player) compileOrUpdateSynth() { if p.synth != nil { - err := p.synth.Update(p.patch) + err := p.synth.Update(p.patch, p.bpm) if err != nil { p.synth = nil p.trySend(PlayerCrashMessage{fmt.Errorf("synth.Update: %w", err)}) @@ -305,7 +308,7 @@ func (p *Player) compileOrUpdateSynth() { } } else { var err error - p.synth, err = p.synthService.Compile(p.patch) + p.synth, err = p.synthService.Compile(p.patch, p.bpm) if err != nil { p.synth = nil p.trySend(PlayerCrashMessage{fmt.Errorf("synthService.Compile: %w", err)}) diff --git a/unittype.go b/unittype.go index 338b444..4aa037a 100644 --- a/unittype.go +++ b/unittype.go @@ -58,7 +58,7 @@ var UnitTypes = map[string]([]UnitParameter){ {Name: "dry", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}, {Name: "feedback", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}, {Name: "damp", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}, - {Name: "notetracking", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}, + {Name: "notetracking", MinValue: 0, MaxValue: 2, CanSet: true, CanModulate: false}, {Name: "delaytime", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true}}, "compressor": []UnitParameter{ {Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}, diff --git a/vm/bytepatch.go b/vm/bytepatch.go index 3e6dea8..cc3e9b7 100644 --- a/vm/bytepatch.go +++ b/vm/bytepatch.go @@ -33,7 +33,7 @@ type SampleOffset struct { LoopLength uint16 } -func Encode(patch sointu.Patch, featureSet FeatureSet) (*BytePatch, error) { +func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*BytePatch, error) { c := BytePatch{PolyphonyBitmask: polyphonyBitmask(patch), NumVoices: uint32(patch.NumVoices())} if c.NumVoices > 32 { return nil, fmt.Errorf("Sointu does not support more than 32 concurrent voices; patch uses %v", c.NumVoices) @@ -42,7 +42,7 @@ func Encode(patch sointu.Patch, featureSet FeatureSet) (*BytePatch, error) { globalAddrs := map[int]uint16{} globalFixups := map[int]([]int){} voiceNo := 0 - delayTable, delayIndices := constructDelayTimeTable(patch) + delayTable, delayIndices := constructDelayTimeTable(patch, bpm) c.DelayTimes = make([]uint16, len(delayTable)) for i := range delayTable { c.DelayTimes[i] = uint16(delayTable[i]) @@ -171,7 +171,7 @@ func Encode(patch sointu.Patch, featureSet FeatureSet) (*BytePatch, error) { if count == 0 { continue // skip encoding delays without any delay lines } - countTrack := count*2 - 1 + unit.Parameters["notetracking"] // 1 means no note tracking and 1 delay, 2 means notetracking with 1 delay, 3 means no note tracking and 2 delays etc. + countTrack := count*2 - 1 + (unit.Parameters["notetracking"] & 1) // 1 means no note tracking and 1 delay, 2 means notetracking with 1 delay, 3 means no note tracking and 2 delays etc. values = append(values, byte(delayIndices[instrIndex][unitIndex]), byte(countTrack)) } c.Commands = append(c.Commands, byte(opcode+unit.Parameters["stereo"])) diff --git a/vm/compiler/bridge/bridge.go b/vm/compiler/bridge/bridge.go index 9e939da..7814b8e 100644 --- a/vm/compiler/bridge/bridge.go +++ b/vm/compiler/bridge/bridge.go @@ -16,17 +16,17 @@ import ( type BridgeService struct { } -func (s BridgeService) Compile(patch sointu.Patch) (sointu.Synth, error) { - synth, err := Synth(patch) +func (s BridgeService) Compile(patch sointu.Patch, bpm int) (sointu.Synth, error) { + synth, err := Synth(patch, bpm) return synth, err } -func Synth(patch sointu.Patch) (*C.Synth, error) { +func Synth(patch sointu.Patch, bpm int) (*C.Synth, error) { s := new(C.Synth) if n := patch.NumDelayLines(); n > 64 { return nil, fmt.Errorf("native bridge has currently a hard limit of 64 delaylines; patch uses %v", n) } - comPatch, err := vm.Encode(patch, vm.AllFeatures{}) + comPatch, err := vm.Encode(patch, vm.AllFeatures{}, bpm) if err != nil { return nil, fmt.Errorf("error compiling patch: %v", err) } @@ -109,11 +109,11 @@ func (s *C.Synth) Release(voice int) { } // Update -func (s *C.Synth) Update(patch sointu.Patch) error { +func (s *C.Synth) Update(patch sointu.Patch, bpm int) error { if n := patch.NumDelayLines(); n > 64 { return fmt.Errorf("native bridge has currently a hard limit of 64 delaylines; patch uses %v", n) } - comPatch, err := vm.Encode(patch, vm.AllFeatures{}) + comPatch, err := vm.Encode(patch, vm.AllFeatures{}, bpm) if err != nil { return fmt.Errorf("error compiling patch: %v", err) } diff --git a/vm/compiler/bridge/bridge_test.go b/vm/compiler/bridge/bridge_test.go index 0ff1839..360584f 100644 --- a/vm/compiler/bridge/bridge_test.go +++ b/vm/compiler/bridge/bridge_test.go @@ -54,7 +54,7 @@ func TestRenderSamples(t *testing.T) { sointu.Unit{Type: "out", Parameters: map[string]int{"stereo": 1, "gain": 128}}, }}} - synth, err := bridge.Synth(patch) + synth, err := bridge.Synth(patch, 120) if err != nil { t.Fatalf("bridge compile error: %v", err) } @@ -130,7 +130,7 @@ func TestStackUnderflow(t *testing.T) { patch := sointu.Patch{sointu.Instrument{NumVoices: 1, Units: []sointu.Unit{ sointu.Unit{Type: "pop", Parameters: map[string]int{}}, }}} - synth, err := bridge.Synth(patch) + synth, err := bridge.Synth(patch, 120) if err != nil { t.Fatalf("bridge compile error: %v", err) } @@ -146,7 +146,7 @@ func TestStackBalancing(t *testing.T) { sointu.Instrument{NumVoices: 1, Units: []sointu.Unit{ sointu.Unit{Type: "push", Parameters: map[string]int{}}, }}} - synth, err := bridge.Synth(patch) + synth, err := bridge.Synth(patch, 120) if err != nil { t.Fatalf("bridge compile error: %v", err) } @@ -179,7 +179,7 @@ func TestStackOverflow(t *testing.T) { sointu.Unit{Type: "pop", Parameters: map[string]int{}}, sointu.Unit{Type: "pop", Parameters: map[string]int{}}, }}} - synth, err := bridge.Synth(patch) + synth, err := bridge.Synth(patch, 120) if err != nil { t.Fatalf("bridge compile error: %v", err) } @@ -196,7 +196,7 @@ func TestDivideByZero(t *testing.T) { sointu.Unit{Type: "invgain", Parameters: map[string]int{"invgain": 0}}, sointu.Unit{Type: "pop", Parameters: map[string]int{}}, }}} - synth, err := bridge.Synth(patch) + synth, err := bridge.Synth(patch, 120) if err != nil { t.Fatalf("bridge compile error: %v", err) } diff --git a/vm/compiler/compiler.go b/vm/compiler/compiler.go index 8049c41..23bcd8f 100644 --- a/vm/compiler/compiler.go +++ b/vm/compiler/compiler.go @@ -84,7 +84,7 @@ func (com *Compiler) Song(song *sointu.Song) (map[string]string, error) { } features := vm.NecessaryFeaturesFor(song.Patch) retmap := map[string]string{} - encodedPatch, err := vm.Encode(song.Patch, features) + encodedPatch, err := vm.Encode(song.Patch, features, song.BPM) if err != nil { return nil, fmt.Errorf(`could not encode patch: %v`, err) } diff --git a/vm/delaytable.go b/vm/delaytable.go index afed1d9..cfdd12b 100644 --- a/vm/delaytable.go +++ b/vm/delaytable.go @@ -107,7 +107,7 @@ func findSuperIntArray(arrays [][]int) ([]int, []int) { // Returns the delay time table and two dimensional array of integers where // element [i][u] is the index for instrument i / unit u in the delay table if // the unit was a delay unit. For non-delay untis, the element is just 0. -func constructDelayTimeTable(patch sointu.Patch) ([]int, [][]int) { +func constructDelayTimeTable(patch sointu.Patch, bpm int) ([]int, [][]int) { ind := make([][]int, len(patch)) var subarrays [][]int // flatten the delay times into one array of arrays @@ -119,11 +119,18 @@ func constructDelayTimeTable(patch sointu.Patch) ([]int, [][]int) { // should use delay times if unit.Type == "delay" { ind[i][j] = len(subarrays) - end := unit.Parameters["count"] - if unit.Parameters["stereo"] > 0 { - end *= 2 + converted := make([]int, len(unit.VarArgs)) + copy(converted, unit.VarArgs) + if unit.Parameters["notetracking"] == 2 { + for i, t := range converted { + delay := 44100 * 60 * t / 48 / bpm + if delay > 65535 { + delay = 65535 + } + converted[i] = delay + } } - subarrays = append(subarrays, unit.VarArgs) + subarrays = append(subarrays, converted) } } } diff --git a/vm/interpreter.go b/vm/interpreter.go index adb24f4..01adeff 100644 --- a/vm/interpreter.go +++ b/vm/interpreter.go @@ -63,8 +63,8 @@ const ( envStateRelease ) -func Synth(patch sointu.Patch) (sointu.Synth, error) { - bytePatch, err := Encode(patch, AllFeatures{}) +func Synth(patch sointu.Patch, bpm int) (sointu.Synth, error) { + bytePatch, err := Encode(patch, AllFeatures{}, bpm) if err != nil { return nil, fmt.Errorf("error compiling %v", err) } @@ -73,8 +73,8 @@ func Synth(patch sointu.Patch) (sointu.Synth, error) { return ret, nil } -func (s SynthService) Compile(patch sointu.Patch) (sointu.Synth, error) { - synth, err := Synth(patch) +func (s SynthService) Compile(patch sointu.Patch, bpm int) (sointu.Synth, error) { + synth, err := Synth(patch, bpm) return synth, err } @@ -87,8 +87,8 @@ func (s *Interpreter) Release(voiceIndex int) { s.synth.voices[voiceIndex].release = true } -func (s *Interpreter) Update(patch sointu.Patch) error { - bytePatch, err := Encode(patch, AllFeatures{}) +func (s *Interpreter) Update(patch sointu.Patch, bpm int) error { + bytePatch, err := Encode(patch, AllFeatures{}, bpm) if err != nil { return fmt.Errorf("error compiling %v", err) } @@ -140,9 +140,11 @@ func (s *Interpreter) Render(buffer []float32, maxtime int) (samples int, time i stereo := channels == 2 opNoStereo := (op & 0xFE) >> 1 if opNoStereo == 0 { - voices = voices[1:] - units = voices[0].units[:] voicesRemaining-- + if voicesRemaining > 0 { + voices = voices[1:] + units = voices[0].units[:] + } if mask := uint32(1) << uint32(voicesRemaining); s.bytePatch.PolyphonyBitmask&mask == mask { commands, values = commandInstr, valuesInstr } else { diff --git a/vm/interpreter_test.go b/vm/interpreter_test.go index 65906ad..aa112c3 100644 --- a/vm/interpreter_test.go +++ b/vm/interpreter_test.go @@ -76,7 +76,7 @@ func TestStackUnderflow(t *testing.T) { patch := sointu.Patch{sointu.Instrument{NumVoices: 1, Units: []sointu.Unit{ sointu.Unit{Type: "pop", Parameters: map[string]int{}}, }}} - synth, err := vm.Synth(patch) + synth, err := vm.Synth(patch, 120) if err != nil { t.Fatalf("bridge compile error: %v", err) } @@ -92,7 +92,7 @@ func TestStackBalancing(t *testing.T) { sointu.Instrument{NumVoices: 1, Units: []sointu.Unit{ sointu.Unit{Type: "push", Parameters: map[string]int{}}, }}} - synth, err := vm.Synth(patch) + synth, err := vm.Synth(patch, 120) if err != nil { t.Fatalf("bridge compile error: %v", err) }