Reorganize the project folder structure and how go packages are organized.

Sointu.asm / lib stuff lives at the root folder. There is a folder called "go4k", which is where
all go stuff lives. Following the ideas from https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1
the go4k folder is the "domain-model" of the go side, and should have no dependencies.
It contains Unit, Instrument, Synth interface etc. Putting go4k under a sub-folder is actually
in the spirit of Ben, as go4k adds dependency to the go language.

Bridge ties the domain-model to the sointulib through cgo. It returns C.Synth, but
makes sure the C.Synth implements the Synth interface, so others are able to use the
Synth no matter how it actually is done. MockSynth and WebProxy synth are good
prospects for other implementations of Synth.

It is a bit fuzzy where methods like "Play" that have no dependencies other than domain
model structs should go. They probably should live in the go4k package as well.

The file-organization on the Go-side is not at all finalized. But how packages are broken
into files is mostly a documentation issue; it does not affect the users of the packages at
all.

BTW: The name go4k was chosen because Ben advocated naming the subpackages
according to the dependency they introduce AND because the prototype of 4klang was
called go4k (there are still some defines in the 4klang source revealing this). go4k thus
honors our roots but is also not so bad name: it's the main package of a 4k synth tracker,
written in go.
This commit is contained in:
Veikko Sariola 2020-10-30 22:05:26 +02:00
parent 23e8bc0c5f
commit e0a793ea6d
116 changed files with 530 additions and 635 deletions

View File

@ -30,8 +30,17 @@ enable_language(ASM_NASM)
# By putting them there, we can pass the same compile definitions to C and ASM
set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> <DEFINES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
# The compiled VSTi is here
add_subdirectory(src)
# Sointu as a header-only library
set(HEADERLIB sointuinterface)
add_library(${HEADERLIB} INTERFACE)
target_include_directories(${HEADERLIB} INTERFACE ${PROJECT_SOURCE_DIR}/include/sointu)
# Sointu as static library
set(STATICLIB sointu)
add_library(${STATICLIB} render.asm)
set_target_properties(${STATICLIB} PROPERTIES LINKER_LANGUAGE C)
target_link_libraries(${STATICLIB} ${HEADERLIB})
target_compile_definitions(${STATICLIB} PUBLIC SU_USE_INTROSPECTION)
# We should put examples here
# add_subdirectory(examples)

View File

@ -1,191 +0,0 @@
package bridge
import (
"errors"
)
// #cgo CFLAGS: -I"${SRCDIR}/../include"
// #cgo LDFLAGS: "${SRCDIR}/../build/src/libsointu.a"
// #include <sointu.h>
import "C"
// SynthState contains the entire state of sointu sound engine
type Synth C.Synth // hide C.Synth, explicit cast is still possible if needed
type SynthState C.SynthState // hide C.Synthstate, explicit cast is still possible if needed
// Opcode is a single byte, representing the virtual machine commands used in Sointu
type Opcode byte
// Unit includes command (filter, oscillator, envelope etc.) and its parameters
type Unit struct {
Command Opcode
Params []byte
}
// Instrument includes a list of units consisting of the instrument, and the number of polyphonic voices for this instrument
type Instrument struct {
NumVoices int
Units []Unit
}
type Patch []Instrument
func (p Patch) TotalVoices() int {
ret := 0
for _, i := range p {
ret += i.NumVoices
}
return ret
}
var ( // cannot be const as the rhs are not known at compile-time
Add = Opcode(C.su_add_id)
Addp = Opcode(C.su_addp_id)
Pop = Opcode(C.su_pop_id)
Loadnote = Opcode(C.su_loadnote_id)
Mul = Opcode(C.su_mul_id)
Mulp = Opcode(C.su_mulp_id)
Push = Opcode(C.su_push_id)
Xch = Opcode(C.su_xch_id)
Distort = Opcode(C.su_distort_id)
Hold = Opcode(C.su_hold_id)
Crush = Opcode(C.su_crush_id)
Gain = Opcode(C.su_gain_id)
Invgain = Opcode(C.su_invgain_id)
Filter = Opcode(C.su_filter_id)
Clip = Opcode(C.su_clip_id)
Pan = Opcode(C.su_pan_id)
Delay = Opcode(C.su_delay_id)
Compres = Opcode(C.su_compres_id)
Advance = Opcode(C.su_advance_id)
Speed = Opcode(C.su_speed_id)
Out = Opcode(C.su_out_id)
Outaux = Opcode(C.su_outaux_id)
Aux = Opcode(C.su_aux_id)
Send = Opcode(C.su_send_id)
Envelope = Opcode(C.su_envelope_id)
Noise = Opcode(C.su_noise_id)
Oscillat = Opcode(C.su_oscillat_id)
Loadval = Opcode(C.su_loadval_id)
Receive = Opcode(C.su_receive_id)
In = Opcode(C.su_in_id)
)
// Stereo returns the stereo version of any (mono or stereo) opcode
func (o Opcode) Stereo() Opcode {
return Opcode(byte(o) | 1) // set lowest bit
}
// Mono returns the mono version of any (mono or stereo) opcode
func (o Opcode) Mono() Opcode {
return Opcode(byte(o) & 0xFE) // clear lowest bit
}
// Render tries to fill the buffer with samples rendered by Sointu.
// Use this version if you are not interested in time modulation. Will always
// fill the buffer.
// Parameters:
// buffer float32 slice to fill with rendered samples. Stereo signal, so
// should have even length.
// Returns an error if something went wrong.
func (synth *Synth) Render(state *SynthState, buffer []float32) error {
if len(buffer)%1 == 1 {
return errors.New("Render writes stereo signals, so buffer should have even length")
}
maxSamples := len(buffer) / 2
state.RandSeed += 1 // if you initialize with empty struct, you will get randseed 1 which is sointu default behavior
errcode := C.su_render((*C.Synth)(synth), (*C.SynthState)(state), (*C.float)(&buffer[0]), C.int(maxSamples))
state.RandSeed -= 1
if errcode > 0 {
return errors.New("Render failed")
}
return nil
}
// RenderTime renders until the buffer is full or the modulated time is reached, whichever
// happens first.
// Parameters:
// buffer float32 slice to fill with rendered samples. Stereo signal, so
// should have even length.
// maxtime how long nominal time to render in samples. Speed unit might modulate time
// so the actual number of samples rendered depends on the modulation and if
// buffer is full before maxtime is reached.
// Returns a tuple (int, int, error), consisting of:
// samples number of samples rendered in the buffer
// time how much the time advanced
// error potential error
// In practice, if nsamples = len(buffer)/2, then time <= maxtime. If maxtime was reached
// first, then nsamples <= len(buffer)/2 and time >= maxtime. Note that it could happen that
// time > maxtime, as it is modulated and the time could advance by 2 or more, so the loop
// exit condition would fire when the time is already past maxtime.
// Under no conditions, nsamples >= len(buffer)/2 i.e. guaranteed to never overwrite the buffer.
func (synth *Synth) RenderTime(state *SynthState, buffer []float32, maxtime int) (int, int, error) {
if len(buffer)%1 == 1 {
return -1, -1, errors.New("RenderTime writes stereo signals, so buffer should have even length")
}
samples := C.int(len(buffer) / 2)
time := C.int(maxtime)
state.RandSeed += 1 // if you initialize with empty struct, you will get randseed 1 which is sointu default behavior
errcode := int(C.su_render_time((*C.Synth)(synth), (*C.SynthState)(state), (*C.float)(&buffer[0]), &samples, &time))
state.RandSeed -= 1
if errcode > 0 {
return -1, -1, errors.New("RenderTime failed")
}
return int(samples), int(time), nil
}
func Compile(patch Patch) (*Synth, error) {
totalVoices := 0
commands := make([]Opcode, 0)
values := make([]byte, 0)
polyphonyBitmask := 0
for _, instr := range patch {
if len(instr.Units) > 63 {
return nil, errors.New("An instrument can have a maximum of 63 units")
}
if instr.NumVoices < 1 {
return nil, errors.New("Each instrument must have at least 1 voice")
}
for _, unit := range instr.Units {
commands = append(commands, unit.Command)
values = append(values, unit.Params...)
}
commands = append(commands, Advance)
totalVoices += instr.NumVoices
for k := 0; k < instr.NumVoices-1; k++ {
polyphonyBitmask = (polyphonyBitmask << 1) + 1
}
polyphonyBitmask <<= 1
}
if totalVoices > 32 {
return nil, errors.New("Sointu does not support more than 32 concurrent voices")
}
if len(commands) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo
return nil, errors.New("The patch would result in more than 2048 commands")
}
if len(values) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo
return nil, errors.New("The patch would result in more than 16384 values")
}
s := new(Synth)
for i := range commands {
s.Commands[i] = (C.uchar)(commands[i])
}
for i := range values {
s.Values[i] = (C.uchar)(values[i])
}
s.NumVoices = C.uint(totalVoices)
s.Polyphony = C.uint(polyphonyBitmask)
return s, nil
}
func (s *SynthState) Trigger(voice int, note byte) {
cs := (*C.SynthState)(s)
cs.SynthWrk.Voices[voice] = C.Voice{}
cs.SynthWrk.Voices[voice].Note = C.int(note)
}
func (s *SynthState) Release(voice int) {
cs := (*C.SynthState)(s)
cs.SynthWrk.Voices[voice].Release = 1
}

148
go4k/bridge/bridge.go Normal file
View File

@ -0,0 +1,148 @@
package bridge
import (
"errors"
"fmt"
"github.com/vsariola/sointu/go4k"
)
// #cgo CFLAGS: -I"${SRCDIR}/../../include/sointu"
// #cgo LDFLAGS: "${SRCDIR}/../../build/libsointu.a"
// #include <sointu.h>
import "C"
type opTableEntry struct {
opcode C.int
parameterList []string
}
var opcodeTable = map[string]opTableEntry{
"add": opTableEntry{C.su_add_id, []string{}},
"addp": opTableEntry{C.su_addp_id, []string{}},
"pop": opTableEntry{C.su_pop_id, []string{}},
"loadnote": opTableEntry{C.su_loadnote_id, []string{}},
"mul": opTableEntry{C.su_mul_id, []string{}},
"mulp": opTableEntry{C.su_mulp_id, []string{}},
"push": opTableEntry{C.su_push_id, []string{}},
"xch": opTableEntry{C.su_xch_id, []string{}},
"distortion": opTableEntry{C.su_distort_id, []string{"drive"}},
"hold": opTableEntry{C.su_hold_id, []string{"holdfreq"}},
"crush": opTableEntry{C.su_crush_id, []string{"resolution"}},
"gain": opTableEntry{C.su_gain_id, []string{"gain"}},
"invgain": opTableEntry{C.su_invgain_id, []string{"invgain"}},
"filter": opTableEntry{C.su_filter_id, []string{"frequency", "resonance", "flags"}},
"clip": opTableEntry{C.su_clip_id, []string{}},
"pan": opTableEntry{C.su_pan_id, []string{"panning"}},
"delay": opTableEntry{C.su_delay_id, []string{"pregain", "dry", "feedback", "depth", "damp", "delay", "count"}},
"compressor": opTableEntry{C.su_compres_id, []string{"attack", "release", "invgain", "threshold", "ratio"}},
"speed": opTableEntry{C.su_speed_id, []string{}},
"out": opTableEntry{C.su_out_id, []string{"gain"}},
"outaux": opTableEntry{C.su_outaux_id, []string{"outgain", "auxgain"}},
"aux": opTableEntry{C.su_aux_id, []string{"gain", "channel"}},
"send": opTableEntry{C.su_send_id, []string{"amount", "port"}},
"envelope": opTableEntry{C.su_envelope_id, []string{"attack", "decay", "sustain", "release", "gain"}},
"noise": opTableEntry{C.su_noise_id, []string{"shape", "gain"}},
"oscillator": opTableEntry{C.su_oscillat_id, []string{"transpose", "detune", "phase", "color", "shape", "gain", "flags"}},
"loadval": opTableEntry{C.su_loadval_id, []string{"value"}},
"receive": opTableEntry{C.su_receive_id, []string{}},
"in": opTableEntry{C.su_in_id, []string{"channel"}},
}
// Render renders until the buffer is full or the modulated time is reached, whichever
// happens first.
// Parameters:
// buffer float32 slice to fill with rendered samples. Stereo signal, so
// should have even length.
// maxtime how long nominal time to render in samples. Speed unit might modulate time
// so the actual number of samples rendered depends on the modulation and if
// buffer is full before maxtime is reached.
// Returns a tuple (int, int, error), consisting of:
// samples number of samples rendered in the buffer
// time how much the time advanced
// error potential error
// In practice, if nsamples = len(buffer)/2, then time <= maxtime. If maxtime was reached
// first, then nsamples <= len(buffer)/2 and time >= maxtime. Note that it could happen that
// time > maxtime, as it is modulated and the time could advance by 2 or more, so the loop
// exit condition would fire when the time is already past maxtime.
// Under no conditions, nsamples >= len(buffer)/2 i.e. guaranteed to never overwrite the buffer.
func (synth *C.Synth) Render(buffer []float32, maxtime int) (int, int, error) {
if len(buffer)%1 == 1 {
return -1, -1, errors.New("RenderTime writes stereo signals, so buffer should have even length")
}
samples := C.int(len(buffer) / 2)
time := C.int(maxtime)
errcode := int(C.su_render(synth, (*C.float)(&buffer[0]), &samples, &time))
if errcode > 0 {
return -1, -1, errors.New("RenderTime failed")
}
return int(samples), int(time), nil
}
func Synth(patch go4k.Patch) (*C.Synth, error) {
totalVoices := 0
commands := make([]byte, 0)
values := make([]byte, 0)
polyphonyBitmask := 0
for insid, instr := range patch {
if len(instr.Units) > 63 {
return nil, errors.New("An instrument can have a maximum of 63 units")
}
if instr.NumVoices < 1 {
return nil, errors.New("Each instrument must have at least 1 voice")
}
for unitid, unit := range instr.Units {
if val, ok := opcodeTable[unit.Type]; ok {
if unit.Stereo {
commands = append(commands, byte(val.opcode+1))
} else {
commands = append(commands, byte(val.opcode))
}
for _, paramname := range val.parameterList {
if pval, ok := unit.Parameters[paramname]; ok {
values = append(values, byte(pval))
} else {
return nil, fmt.Errorf("Unit parameter undefined: %v (at instrument %v, unit %v)", paramname, insid, unitid)
}
}
} else {
return nil, fmt.Errorf("Unknown unit type: %v (at instrument %v, unit %v)", unit.Type, insid, unitid)
}
}
commands = append(commands, byte(C.su_advance_id))
totalVoices += instr.NumVoices
for k := 0; k < instr.NumVoices-1; k++ {
polyphonyBitmask = (polyphonyBitmask << 1) + 1
}
polyphonyBitmask <<= 1
}
if totalVoices > 32 {
return nil, errors.New("Sointu does not support more than 32 concurrent voices")
}
if len(commands) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo
return nil, errors.New("The patch would result in more than 2048 commands")
}
if len(values) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo
return nil, errors.New("The patch would result in more than 16384 values")
}
s := new(C.Synth)
for i := range commands {
s.Commands[i] = (C.uchar)(commands[i])
}
for i := range values {
s.Values[i] = (C.uchar)(values[i])
}
s.NumVoices = C.uint(totalVoices)
s.Polyphony = C.uint(polyphonyBitmask)
s.RandSeed = 1
return s, nil
}
func (s *C.Synth) Trigger(voice int, note byte) {
s.SynthWrk.Voices[voice] = C.Voice{}
s.SynthWrk.Voices[voice].Note = C.int(note)
}
func (s *C.Synth) Release(voice int) {
s.SynthWrk.Voices[voice].Release = 1
}

View File

@ -8,7 +8,8 @@ import (
"runtime"
"testing"
"github.com/vsariola/sointu/bridge"
"github.com/vsariola/sointu/go4k"
"github.com/vsariola/sointu/go4k/bridge"
)
const BPM = 100
@ -21,30 +22,29 @@ const su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS
// const bufsize = su_max_samples * 2
func TestBridge(t *testing.T) {
patch := []bridge.Instrument{
bridge.Instrument{1, []bridge.Unit{
bridge.Unit{bridge.Envelope, []byte{64, 64, 64, 80, 128}},
bridge.Unit{bridge.Envelope, []byte{95, 64, 64, 80, 128}},
bridge.Unit{bridge.Out.Stereo(), []byte{128}},
patch := []go4k.Instrument{
go4k.Instrument{1, []go4k.Unit{
go4k.Unit{"envelope", false, map[string]int{"attack": 64, "decay": 64, "sustain": 64, "release": 80, "gain": 128}},
go4k.Unit{"envelope", false, map[string]int{"attack": 95, "decay": 64, "sustain": 64, "release": 80, "gain": 128}},
go4k.Unit{"out", true, map[string]int{"gain": 128}},
}}}
synth, err := bridge.Compile(patch)
synth, err := bridge.Synth(patch)
if err != nil {
t.Fatalf("bridge compile error: %v", err)
}
var state bridge.SynthState
state.Trigger(0, 64)
synth.Trigger(0, 64)
buffer := make([]float32, 2*su_max_samples)
err = synth.Render(&state, buffer[:len(buffer)/2])
err = go4k.Render(synth, buffer[:len(buffer)/2])
if err != nil {
t.Fatalf("first render gave an error")
}
state.Release(0)
err = synth.Render(&state, buffer[len(buffer)/2:])
synth.Release(0)
err = go4k.Render(synth, buffer[len(buffer)/2:])
if err != nil {
t.Fatalf("first render gave an error")
}
_, filename, _, _ := runtime.Caller(0)
expectedb, err := ioutil.ReadFile(path.Join(path.Dir(filename), "..", "tests", "expected_output", "test_render_samples.raw"))
expectedb, err := ioutil.ReadFile(path.Join(path.Dir(filename), "..", "..", "tests", "expected_output", "test_render_samples.raw"))
if err != nil {
t.Fatalf("cannot read expected: %v", err)
}

49
go4k/go4k.go Normal file
View File

@ -0,0 +1,49 @@
package go4k
import (
"errors"
"math"
)
// Unit is e.g. a filter, oscillator, envelope and its parameters
type Unit struct {
Type string
Stereo bool
Parameters map[string]int
}
// Instrument includes a list of units consisting of the instrument, and the number of polyphonic voices for this instrument
type Instrument struct {
NumVoices int
Units []Unit
}
// Patch is simply a list of instruments used in a song
type Patch []Instrument
func (p Patch) TotalVoices() int {
ret := 0
for _, i := range p {
ret += i.NumVoices
}
return ret
}
type Track struct {
NumVoices int
Sequence []byte
}
type Synth interface {
Render(buffer []float32, maxtime int) (int, int, error)
Trigger(voice int, note byte)
Release(voice int)
}
func Render(synth Synth, buffer []float32) error {
s, _, err := synth.Render(buffer, math.MaxInt32)
if s != len(buffer)/2 {
return errors.New("synth.Render should have filled the whole buffer")
}
return err
}

View File

@ -1,38 +1,41 @@
package song
package go4k
import (
"errors"
"github.com/vsariola/sointu/bridge"
)
type Track struct {
NumVoices int
Sequence []byte
}
import "errors"
type Song struct {
BPM int
Patterns [][]byte
Tracks []Track
Patch bridge.Patch
Samples int // -1 means calculate automatically, but you can also set it manually
BPM int
Patterns [][]byte
Tracks []Track
SongLength int // in samples, 0 means calculate automatically from BPM and Track lengths, but can also set manually
Patch Patch
}
func NewSong(bpm int, patterns [][]byte, tracks []Track, patch bridge.Patch) (*Song, error) {
s := new(Song)
s.BPM = bpm
s.Patterns = patterns
s.Tracks = tracks
s.Patch = patch
err := s.Validate()
if err != nil {
return nil, err
func (s *Song) PatternRows() int {
return len(s.Patterns[0])
}
func (s *Song) SequenceLength() int {
return len(s.Tracks[0].Sequence)
}
func (s *Song) TotalRows() int {
return s.PatternRows() * s.SequenceLength()
}
func (s *Song) SamplesPerRow() int {
return 44100 * 60 / (s.BPM * 4)
}
func (s *Song) FirstTrackVoice(track int) int {
ret := 0
for _, t := range s.Tracks[:track] {
ret += t.NumVoices
}
s.Samples = -1
return s, nil
return ret
}
// TBD: Where shall we put methods that work on pure domain types and have no dependencies
// e.g. Validate here
func (s *Song) Validate() error {
if s.BPM < 1 {
return errors.New("BPM should be > 0")
@ -62,66 +65,42 @@ func (s *Song) Validate() error {
return nil
}
func (s *Song) PatternRows() int {
return len(s.Patterns[0])
}
func (s *Song) SequenceLength() int {
return len(s.Tracks[0].Sequence)
}
func (s *Song) TotalRows() int {
return s.PatternRows() * s.SequenceLength()
}
func (s *Song) SamplesPerRow() int {
return 44100 * 60 / (s.BPM * 4)
}
func (s *Song) FirstTrackVoice(track int) int {
ret := 0
for _, t := range s.Tracks[:track] {
ret += t.NumVoices
}
return ret
}
func (s *Song) Render(synth *bridge.Synth, state *bridge.SynthState) ([]float32, error) {
err := s.Validate()
func Play(synth Synth, song Song) ([]float32, error) {
err := song.Validate()
if err != nil {
return nil, err
}
curVoices := make([]int, len(s.Tracks))
curVoices := make([]int, len(song.Tracks))
for i := range curVoices {
curVoices[i] = s.FirstTrackVoice(i)
curVoices[i] = song.FirstTrackVoice(i)
}
samples := s.Samples
if samples < 0 {
samples = s.TotalRows() * s.SamplesPerRow()
samples := song.SongLength
if samples <= 0 {
samples = song.TotalRows() * song.SamplesPerRow()
}
buffer := make([]float32, samples*2)
totaln := 0
rowtime := 44100 * 60 / (s.BPM * 4)
for row := 0; row < s.TotalRows(); row++ {
patternRow := row % s.PatternRows()
pattern := row / s.PatternRows()
for t := range s.Tracks {
patternIndex := s.Tracks[t].Sequence[pattern]
note := s.Patterns[patternIndex][patternRow]
rowtime := song.SamplesPerRow()
for row := 0; row < song.TotalRows(); row++ {
patternRow := row % song.PatternRows()
pattern := row / song.PatternRows()
for t := range song.Tracks {
patternIndex := song.Tracks[t].Sequence[pattern]
note := song.Patterns[patternIndex][patternRow]
if note == 1 { // anything but hold causes an action.
continue // TODO: can hold be actually something else than 1?
}
state.Release(curVoices[t])
synth.Release(curVoices[t])
if note > 1 {
curVoices[t]++
first := s.FirstTrackVoice(t)
if curVoices[t] >= first+s.Tracks[t].NumVoices {
first := song.FirstTrackVoice(t)
if curVoices[t] >= first+song.Tracks[t].NumVoices {
curVoices[t] = first
}
state.Trigger(curVoices[t], note)
synth.Trigger(curVoices[t], note)
}
}
samples, _, _ := synth.RenderTime(state, buffer[2*totaln:], rowtime)
samples, _, _ := synth.Render(buffer[2*totaln:], rowtime)
totaln += samples
}
return buffer, nil

View File

@ -1,4 +1,4 @@
package song_test
package go4k_test
import (
"bytes"
@ -8,8 +8,9 @@ import (
"runtime"
"testing"
"github.com/vsariola/sointu/bridge"
"github.com/vsariola/sointu/song"
"github.com/vsariola/sointu/go4k"
"github.com/vsariola/sointu/go4k/bridge"
// TODO: test the song using a mocks instead
)
const BPM = 100
@ -21,29 +22,24 @@ const su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS
// const bufsize = su_max_samples * 2
func TestSongRender(t *testing.T) {
patch := []bridge.Instrument{
bridge.Instrument{1, []bridge.Unit{
bridge.Unit{bridge.Envelope, []byte{32, 32, 64, 64, 128}},
bridge.Unit{bridge.Oscillat, []byte{64, 64, 0, 96, 64, 128, 0x40}},
bridge.Unit{bridge.Mulp, []byte{}},
bridge.Unit{bridge.Envelope, []byte{32, 32, 64, 64, 128}},
bridge.Unit{bridge.Oscillat, []byte{72, 64, 64, 64, 96, 128, 0x40}},
bridge.Unit{bridge.Mulp, []byte{}},
bridge.Unit{bridge.Out.Stereo(), []byte{128}},
}}}
func TestPlayer(t *testing.T) {
patch := go4k.Patch{go4k.Instrument{1, []go4k.Unit{
go4k.Unit{"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
go4k.Unit{"oscillator", false, map[string]int{"transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "flags": 0x40}},
go4k.Unit{"mulp", false, map[string]int{}},
go4k.Unit{"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
go4k.Unit{"oscillator", false, map[string]int{"transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "flags": 0x40}},
go4k.Unit{"mulp", false, map[string]int{}},
go4k.Unit{"out", true, map[string]int{"gain": 128}},
}}}
patterns := [][]byte{{64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0}}
tracks := []song.Track{song.Track{1, []byte{0}}}
song, err := song.NewSong(100, patterns, tracks, patch)
if err != nil {
t.Fatalf("NewSong failed: %v", err)
}
synth, err := bridge.Compile(patch)
tracks := []go4k.Track{go4k.Track{1, []byte{0}}}
song := go4k.Song{100, patterns, tracks, 0, patch}
synth, err := bridge.Synth(patch)
if err != nil {
t.Fatalf("Compiling patch failed: %v", err)
}
var state bridge.SynthState
buffer, err := song.Render(synth, &state)
buffer, err := go4k.Play(synth, song)
if err != nil {
t.Fatalf("Render failed: %v", err)
}

View File

@ -30,14 +30,11 @@ typedef struct SynthWorkspace {
struct Voice Voices[32];
} SynthWorkspace;
typedef struct SynthState {
typedef struct Synth {
struct SynthWorkspace SynthWrk;
struct DelayWorkspace DelayWrks[64]; // let's keep this as 64 for now, so the delays take 16 meg. If that's too little or too much, we can change this in future.
unsigned int RandSeed;
unsigned int GlobalTick;
} SynthState;
typedef struct Synth {
unsigned char Commands[32 * 64];
unsigned char Values[32 * 64 * 8];
unsigned int Polyphony;
@ -59,35 +56,14 @@ typedef struct Synth {
extern void CALLCONV su_load_gmdls(void);
#endif
// int su_render(Synth* synth,SynthState* synthState, float* buffer, int samples):
// Renders 'samples' number of 'samples' to the 'buffer', using 'synth'.
// Modifies 'synthState' and fills the 'buffer'.
//
// Parameters:
// synth pointer to the synthesizer used. Won't get modified by the call.
// synthState pointer to current synthState. RandSeed should be > 0 e.g. 1
// buffer audio sample buffer, L R L R ...
// samples maximum number of samples to be rendered. WARNING: buffer
// should have a length of 2 * samples as the audio is stereo.
//
// Returns error code:
// 0 everything ok
// (returns always 0 as no errors are implemented yet)
int CALLCONV su_render(Synth* synth,SynthState* synthState, float* buffer, int samples);
// int su_render_time(Synth* synth,SynthState* synthState, float* buffer, int* samples, int* time):
// int su_render(Synth* synth, float* buffer, int* samples, int* time):
// Renders samples until 'samples' number of samples are reached or 'time' number of
// modulated time ticks are reached, whichever happens first. 'samples' and 'time' are
// are passed by reference as the function modifies to tell how many samples were
// actually rendered and how many time ticks were actually advanced.
//
// Parameters:
// synth pointer to the synthesizer used. Won't get modified by the call.
// synthState pointer to current synthState. RandSeed should be > 0 e.g. 1
// Also synthState->SamplesPerRow cannot be 0 or nothing will be
// rendered; either set it to INT32_MAX to always render full
// buffer, or something like SAMPLE_RATE * 60 / (BPM * 4) for
// having 4 rows per beat.
// synth pointer to the synthesizer used. RandSeed should be > 0 e.g. 1
// buffer audio sample buffer, L R L R ...
// samples pointer to the maximum number of samples to be rendered.
// buffer should have a length of 2 * maxsamples as the audio
@ -104,7 +80,7 @@ int CALLCONV su_render(Synth* synth,SynthState* synthState, float* buffer, int s
// Returns error code:
// 0 everything ok
// (no actual errors implemented yet)
int CALLCONV su_render_time(Synth* synth,SynthState* synthState, float* buffer, int* samples, int* time);
int CALLCONV su_render(Synth* synth, float* buffer, int* samples, int* time);
// Arithmetic opcode ids
extern const int su_add_id;

View File

@ -25,14 +25,11 @@ USE_OUT
section .text
struc su_synth_state
struc su_synth
.synthwrk resb su_synthworkspace.size
.delaywrks resb su_delayline_wrk.size * 64
.randseed resd 1
.globaltime resd 1
endstruc
struc su_synth
.globaltime resd 1
.commands resb 32 * 64
.values resb 32 * 64 * 8
.polyphony resd 1
@ -41,35 +38,25 @@ endstruc
SECT_TEXT(sursampl)
EXPORT MANGLE_FUNC(su_render_time,20)
EXPORT MANGLE_FUNC(su_render,16)
%if BITS == 32 ; stdcall
pushad ; push registers
mov eax, [esp + 4 + 32] ; eax = &synth
mov ecx, [esp + 8 + 32] ; ecx = &synthState
mov edx, [esp + 12 + 32] ; edx = &buffer
mov esi, [esp + 16 + 32] ; esi = &samples
mov ebx, [esp + 20 + 32] ; ebx = &time
pushad ; push registers
mov ecx, [esp + 4 + 32] ; ecx = &synthState
mov edx, [esp + 8 + 32] ; edx = &buffer
mov esi, [esp + 12 + 32] ; esi = &samples
mov ebx, [esp + 16 + 32] ; ebx = &time
%else
%ifidn __OUTPUT_FORMAT__,win64
; win64 ABI parameter order: RCX, RDX, R8, R9; rest in stack (right-to-left, note shadow space!)
; here: rcx = &synth, rdx = &synthstate, r8 = &buffer, r9 = &bufsize, stack1 = &time
%ifidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rcx = &synth, rdx = &buffer, r8 = &bufsize, r9 = &time
push_registers rdi, rsi, rbx, rbp ; win64 ABI: these registers are non-volatile
mov rbx, [rsp + 0x28 + PUSH_REG_SIZE(4)] ; rbx = &time, note the shadow space so &time is at rsp + 0x28
mov rax, rcx
mov rcx, rdx
mov rdx, r8
mov rsi, r9
%else
; System V ABI parameter order: RDI, RSI, RDX, RCX, R8, R9; rest in stack (right-to-left)
; here: rdi = &synth, rsi = &synthstate, rdx = &buffer, rcx = &samples, r8 = &time
mov rsi, r8 ; rsi = &samples
mov rbx, r9 ; rbx = &time
%else ; System V ABI: rdi = &synth, rsi = &buffer, rdx = &samples, rcx = &time
push_registers rbx, rbp ; System V ABI: these registers are non-volatile
mov rax, rdi
mov rbx, r8
xchg rcx, rsi
mov rbx, rcx ; rbx points to time
xchg rsi, rdx ; rdx points to buffer, rsi points to samples
mov rcx, rdi ; rcx = &Synthstate
%endif
%endif
; _AX = &synth, _BX == &time, _CX == &synthstate, _DX == &buf, _SI == &samples,
push _AX ; push the pointer to synth to stack
push _SI ; push the pointer to samples
push _BX ; push the pointer to time
xor eax, eax ; samplenumber starts at 0
@ -78,9 +65,9 @@ EXPORT MANGLE_FUNC(su_render_time,20)
push _SI ; push bufsize
push _DX ; push bufptr
push _CX ; this takes place of the voicetrack
mov eax, [_CX + su_synth_state.randseed]
mov eax, [_CX + su_synth.randseed]
push _AX ; randseed
mov eax, [_CX + su_synth_state.globaltime]
mov eax, [_CX + su_synth.globaltime]
push _AX ; global tick time
mov ebx, dword [_BX] ; zero extend dereferenced pointer
push _BX ; the nominal rowlength should be time_in
@ -93,24 +80,23 @@ su_render_samples_loop:
jge su_render_samples_time_finish ; goto finish
inc eax ; time++
inc dword [_SP + PTRSIZE*6] ; samples++
mov _CX, [_SP + PTRSIZE*3] ; _CX = &synthstate
mov _BP, [_SP + PTRSIZE*9] ; _BP = &synth
mov _CX, [_SP + PTRSIZE*3]
push _AX ; push rowtick
mov eax, [_BP + su_synth.polyphony]
mov eax, [_CX + su_synth.polyphony]
push _AX ;polyphony
mov eax, [_BP + su_synth.numvoices]
mov eax, [_CX + su_synth.numvoices]
push _AX ;numvoices
lea _DX, [_CX + su_synth_state.synthwrk]
lea COM, [_BP + su_synth.commands]
lea VAL, [_BP + su_synth.values]
lea WRK, [_DX + su_synthworkspace.voices] ; _BP and WRK are actually the same thing so here _BP gets overwritten
lea _CX, [_CX + su_synth_state.delaywrks - su_delayline_wrk.filtstate]
lea _DX, [_CX+ su_synth.synthwrk]
lea COM, [_CX+ su_synth.commands]
lea VAL, [_CX+ su_synth.values]
lea WRK, [_DX + su_synthworkspace.voices]
lea _CX, [_CX+ su_synth.delaywrks - su_delayline_wrk.filtstate]
call MANGLE_FUNC(su_run_vm,0)
pop _AX
pop _AX
mov _DI, [_SP + PTRSIZE*5] ; edi containts buffer ptr
mov _CX, [_SP + PTRSIZE*4]
lea _SI, [_CX + su_synth_state.synthwrk + su_synthworkspace.left]
lea _SI, [_CX + su_synth.synthwrk + su_synthworkspace.left]
movsd ; copy left channel to output buffer
movsd ; copy right channel to output buffer
mov [_SP + PTRSIZE*5], _DI ; save back the updated ptr
@ -126,8 +112,8 @@ su_render_samples_time_finish:
pop _BX
pop _DX
pop _CX
mov [_CX + su_synth_state.randseed], edx
mov [_CX + su_synth_state.globaltime], ebx
mov [_CX + su_synth.randseed], edx
mov [_CX + su_synth.globaltime], ebx
pop _BX
pop _BX
pop _DX
@ -135,12 +121,11 @@ su_render_samples_time_finish:
pop _SI ; pop the pointer to samples
mov dword [_SI], edx ; *samples = samples rendered
mov dword [_BX], eax ; *time = time ticks rendered
pop _AX ; pop the synth pointer
xor eax, eax ; TODO: set eax to possible error code, now just 0
%if BITS == 32 ; stdcall
mov [_SP + 28],eax ; we want to return eax, but popad pops everything, so put eax to stack for popad to pop
popad
ret 20
ret 16
%else
%ifidn __OUTPUT_FORMAT__,win64
pop_registers rdi, rsi, rbx, rbp ; win64 ABI: these registers are non-volatile
@ -149,45 +134,3 @@ su_render_samples_time_finish:
%endif
ret
%endif
EXPORT MANGLE_FUNC(su_render,16)
%if BITS == 32 ; stdcall
mov eax, 0x7FFFFFFF ; don't care about time, just try to fill the buffer
push eax
push esp
lea eax, [esp + 24]
push eax
push dword [esp + 24]
push dword [esp + 24]
push dword [esp + 24]
%else
%ifidn __OUTPUT_FORMAT__,win64
; win64 ABI parameter order: RCX, RDX, R8, R9; rest in stack (right-to-left, note shadow space!)
; here: rcx = &synth, rdx = &synthstate, r8 = &buffer, r9 = samples
; put the values in shadow space and get pointers to them
mov [_SP+0x8],r9
lea r9, [_SP+0x8]
mov [_SP+0x10], dword 0x7FFFFFFF ; don't care about time, just try to fill the buffer
lea r10, [_SP+0x10]
mov [_SP+0x20], r10
%else
; System V ABI parameter order: RDI, RSI, RDX, RCX, R8, R9; rest in stack (right-to-left)
; here: rdi = &synth, rsi = &synthstate, rdx = &buffer, rcx = samples
push rcx
mov rcx, _SP ; pass a pointer to samples instead of direct value
mov r8, 0x7FFFFFFF ; don't care about time, just try to fill the buffer
push r8
mov r8, _SP ; still, we have to pass a pointer to time, so pointer to stack
%endif
%endif
call MANGLE_FUNC(su_render_time,20)
%if BITS == 32 ; stdcall
pop ecx
ret 16
%else
%ifnidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rdx = bufsize, r8 = &buffer, rcx = &synthstate
pop rcx
pop r8
%endif
ret
%endif

View File

@ -1,12 +0,0 @@
set(LIB sointu)
set(SOURCES sointu.asm)
# Headers
include_directories(${PROJECT_SOURCE_DIR}/include)
# Library target
add_library(${LIB} ${SOURCES})
set_target_properties(${LIB} PROPERTIES LINKER_LANGUAGE C)
target_link_libraries(${LIB})
target_compile_definitions(${LIB} PUBLIC SU_USE_INTROSPECTION)

View File

@ -21,6 +21,7 @@ function(regression_test testname)
add_test(${testname} ${testname})
target_compile_definitions(${testname} PUBLIC TEST_NAME="${testname}" SU_USE_INTROSPECTION SU_USE_PLAYER)
target_link_libraries(${testname} ${HEADERLIB})
set (rawinput ${CMAKE_CURRENT_SOURCE_DIR}/expected_output/${testname}.raw)
set (rawoutput ${CMAKE_CURRENT_BINARY_DIR}/expected_output/${testname}.raw)
@ -151,8 +152,8 @@ regression_test(test_chords "ENVELOPE;VCO_SINE")
regression_test(test_speed "ENVELOPE;VCO_SINE")
regression_test(test_render_samples ENVELOPE "" test_render_samples.c)
target_link_libraries(test_render_samples sointu)
target_link_libraries(test_render_samples ${STATICLIB})
add_executable(test_render_samples_api test_render_samples_api.c)
target_link_libraries(test_render_samples_api sointu)
target_link_libraries(test_render_samples_api ${STATICLIB})
add_test(test_render_samples_api test_render_samples_api)

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0,
@ -19,4 +19,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0,
@ -24,4 +24,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -22,4 +22,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -19,4 +19,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -25,4 +25,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 0, 0, 68, 0, 0, 0, 66, 0, 0, 0, 69, 0, 0, 0,
@ -25,4 +25,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -24,4 +24,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -24,4 +24,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65,
@ -39,4 +39,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65,
@ -39,4 +39,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -24,4 +24,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -23,4 +23,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -25,4 +25,4 @@ BEGIN_DELTIMES
DELTIME 11025
END_DELTIMES
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -27,4 +27,4 @@ BEGIN_DELTIMES
DELTIME 11025
END_DELTIMES
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -27,4 +27,4 @@ BEGIN_DELTIMES
DELTIME 11025
END_DELTIMES
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -27,4 +27,4 @@ BEGIN_DELTIMES
DELTIME 11025
END_DELTIMES
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,7 +1,7 @@
%define BPM 100
%define INCLUDE_DELAY_MODULATION
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 80, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0
@ -28,4 +28,4 @@ BEGIN_DELTIMES
DELTIME 1000
END_DELTIMES
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -31,4 +31,4 @@ BEGIN_DELTIMES
DELTIME 10787
END_DELTIMES
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -27,4 +27,4 @@ BEGIN_DELTIMES
DELTIME 11025
END_DELTIMES
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -25,4 +25,4 @@ BEGIN_DELTIMES
DELTIME 1116,1188,1276,1356,1422,1492,1556,1618
END_DELTIMES
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -26,4 +26,4 @@ BEGIN_DELTIMES
DELTIME 21025
END_DELTIMES
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -20,4 +20,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -23,4 +23,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -18,4 +18,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -18,4 +18,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD,HLD, HLD, HLD, 0, 0, 0, 0, 0,
@ -24,4 +24,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -17,4 +17,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -23,4 +23,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -23,4 +23,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -19,4 +19,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -20,4 +20,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -20,4 +20,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -23,4 +23,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -19,4 +19,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -24,4 +24,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -23,4 +23,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -20,4 +20,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -19,4 +19,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0
@ -18,4 +18,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0
@ -17,4 +17,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -18,4 +18,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -17,4 +17,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -19,4 +19,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -24,4 +24,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -22,4 +22,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64,HLD,HLD,HLD,HLD,HLD,HLD,HLD, 0, 0, 0,0,0,0,0,0,
@ -25,4 +25,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -22,4 +22,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -20,4 +20,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 80, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0
@ -22,4 +22,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 80, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0
@ -22,4 +22,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 80, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0
@ -22,4 +22,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 80, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0
@ -22,4 +22,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 0,0,0,0,0,0,0,0,
@ -39,4 +39,4 @@ BEGIN_SAMPLE_OFFSETS
SAMPLE_OFFSET START(1680142),LOOPSTART(1483),LOOPLENGTH(95) ; name VIOLN70, unitynote 58 (transpose to 2), data length 1579
END_SAMPLE_OFFSETS
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 0,0,0,0,0,0,0,0,
@ -36,4 +36,4 @@ BEGIN_SAMPLE_OFFSETS
SAMPLE_OFFSET START(1678611),LOOPSTART(1341),LOOPLENGTH(106) ; name VIOLN68, unitynote 56 (transpose to 4), data length 1448
END_SAMPLE_OFFSETS
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 80, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0
@ -22,4 +22,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -20,4 +20,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 80, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0
@ -22,4 +22,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -21,4 +21,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -20,4 +20,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
@ -19,4 +19,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -23,4 +23,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -24,4 +24,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

View File

@ -1,6 +1,6 @@
%define BPM 100
%include "../src/sointu_header.inc"
%include "sointu_header.inc"
BEGIN_PATTERNS
PATTERN 64, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0
@ -18,4 +18,4 @@ BEGIN_PATCH
END_INSTRUMENT
END_PATCH
%include "../src/sointu_footer.inc"
%include "sointu_footer.inc"

Some files were not shown because too many files have changed in this diff Show More