add clinkster source (doesn't seem to have a public git repo)

This commit is contained in:
PoroCYon 2019-05-11 01:01:15 +02:00
parent 43cccc563e
commit 718ba6f4f2
42 changed files with 4980 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
clinkster/Clinkster/* linguist-vendored

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.o

205
clinkster/Clinkster/clinkster.txt vendored Normal file
View File

@ -0,0 +1,205 @@
CLINKSTER - a software synthesizer for 4k intros by Blueberry / Loonies
INTRODUCTION
Clinkster is a software synthesizer (or synth, for short) designed for use in
extremely size-restricted executables, such a 4k intros. It has been under
development since 2008 and used in several 4k intros through the years. For
some background reading on the ideas behind it and its early history, refer
to the article from Zine #14 "Development Diary of Luminagia":
http://zine.bitfellas.org/article.php?zine=14&id=24
FEATURES
- VST instrument for Windows and Mac OS X, with source
- Simple interface: create your instruments using just 18 sliders
- Multi-layered, delicious, voluminous stereo sound based on phase modulation
- Unlimited number of tracks and unlimited polyphony per track
- Player source for Windows - integrate with C++ or asm
- Excludes unused features from player code to save space
- Easy-exe setup for creating executable music - no coding or additional
installation needed
- Many example songs and instruments included
OVERVIEW
The Clinkster toolchain consists of three parts:
1. A VST instrument to use when composing the music. This can in principle be
used with any VSTi host, but the rest of the toolchain is designed to be
used with Renoise, so if you want to use your music in an executable, you
will need to make your music in Renoise.
2. A conversion script - RenoiseConvert.py - to convert a Renoise song using
Clinkster instruments into a .asm file containing the music data in the
format needed by the executable player. To run the conversion script, you
will need to install Python 2.x (where x >= 5).
3. Player code to include in your intro. Two versions are provided:
clinkster.asm and clinkster_multithreaded.asm. The multithreaded version
is bigger but computes the music twice as fast (provided you have at least
two CPU cores). Both of these assume the converted music in music.asm.
The clinkster.h and clinkster.inc files contain definitions for using the
synth from C/C++ or asm, respectively. Refer to these files for detailed
usage information.
PARAMETERS
Clinkster is designed to be used with the built-in VST parameter adjustment
GUI in Renoise.
The numbers in parentheses after the value of each parameter indicates the
byte value that will be used to represent that parameter value in the
executable version of the music. Using the same byte values across parameters
and across instruments generally leads to a more compact representation of the
music.
The sliders in Renoise have 101 different positions, and for most of the
parameters, these correspond to successive byte values. The exceptions are:
- B PitchD, M PitchD, IndexD, Attack, Decay, Release, Gain: One position for
every two values.
- B Pitch and M Pitch: 5 positions for every 12 values (one octave).
- RandomSeed: 128 values in total.
If you want a value in between two slider-accessible values, click on the
value to access the internal representation (0% for minimum, 100% for maximum)
and modify it slightly.
As for the meaning of the individual parameters, experimentation is the key.
Be sure to test your instrument in many different octaves. To get you started,
here is a brief description of the parameters:
BaseWave, ModWave: Waveforms for the two oscillators. The ModWave modulates
the phase of the BaseWave.
B Detune, M Detune: Randomly varies the frequencies of the two oscillators
across layers and across the left and right channel.
B Pitch, M Pitch: Pitch the oscillator up or down relative to the played note.
B PitchD, M PitchD (pitch decay): Decay the pitch towards (or away from) the
played note.
Index: Strength of the phase modulation.
IndexSpr (index spread): Randomly varies the modulation strength across layers
and across the left and right channel.
IndexD (index decay): Decay the modulation strength towards (or away from)
zero.
Layers: Number of layers of sound to compute, with individual, random
variations controlled by the detune and index spread parameters. More
layers give a fatter, more chorus-like sound but also increases the
computational load of the instrument considerably.
RandomSeed: Seed used for the random variations across layers and channels.
Adjust this until you get a balanced, in-key sound.
Attack, Decay, Sustain, Release: Control the amplitude envelope of the sound.
Setting Sustain to a negative value creates a "double attack" where the
amplitude crosses zero during the decay.
Gain: Amplify the sound after the amplitude envelope and apply soft clipping.
Results in distorted or compressed sounds.
Also take a look at the included example instruments and example songs for
inspiration.
USING THE VST
In order to use the music in an executable, there are a couple of guidelines
you need to follow when creating your music in Renoise.
You can use per-note velocity, but no other in-track effect commands.
You can use any number of tracks and any number of note columns per track.
You can adjust the volume and panning of tracks using the Volume/Panning Track
DSP, the post-DSP volume/panning, the mixer and the master volume.
You can use the Delay effect (under Track DSPs) with these restrictions:
- The "L Feedb.", "R Feedb." and "Send" sliders must be at the same position.
- "Mute Src." must be off.
- No L/R Output Pan.
- If you use the Delay effect on multiple tracks, you must use the exact same
parameters on each track.
To make it easier to control the volume and panning of a group of tracks, or
to use the same delay on multiple tracks, you can use a #Send DSP set to "Mute
Source" to route the sound from the track to a Send track. You can then set
the desired volume, panning and delay on the Send track. Only the final track
of a Send chain can use delay.
The VST is designed to run in one instance per instrument and supports
unlimited polyphony and unlimited reuse of instruments across tracks. Note
however, that if you play notes using the same instrument in multiple tracks
at the same time, Renoise will play the notes using a single VST instance and
output the result in the track in which the last note was triggered. This
works fine as long as the tracks use the same volume, panning and delay. If
this is not the case, you will need to use separate copies of the instrument
for each different track. Otherwise, you will get clicks and generally a
different result from what you expected.
In the pattern sequence matrix (available to the left of the pattern view),
you can mute individual tracks at specific positions. This muting is taken
into account by the converter.
The VST works with any sample rate. To match the sound produced by the player,
use a sample rate of 44100Hz. If your music is too computationally heavy for
your CPU to cope with, you can lighten the load by lowering the sample rate
while composing, though that will of course have a detrimental effect on the
sound quality.
USING THE CONVERTER
The RenoiseConvert.py script in the converter directory (or, equivalently,
RenoiseConvert.exe in easy_exe/tools) will convert a Renoise song using
Clinkster into a .asm file to use with the supplied player source.
During conversion, each note column in a track will become a separate track in
the converted music. If you use more than one instrument inside a single note
column, that column will be split by the converter into one track per
instrument. Also, if you have used the delay effect, all tracks using delay
will be put before the tracks not using delay.
The converter will print a list of the resulting tracks, along with the
original track / instrument combinations they correspond to.
For each track, it will print a list of tone / length / velocity combinations
used in that track, in the form NOTE/VELOCITY:LENGTH(NUMBER) or
NOTE:LENGTH(NUMBER). For instance, C-4/6A:4(32) means that C-4 notes with
velocity 6A (hex) and length 4 occur 32 times in the track. If the velocity is
omitted, it is 127 (7F hex - maximum). The number of different combinations
used in the track has influence on the size of the resulting music (as well as
its precalculation time), so this list can be useful as a guide for optimizing
your music to take up less space.
At the end, the converter prints a list of the optional features used in the
music. Each of these features has some cost in terms of the code size of the
player. The options are:
SINE, SAWTOOTH, SQUARE, PARABOLA, TRIANGLE, NOISE:
The corresponding waveform is used in some instrument.
VELOCITY: One or more notes have velocity less than 127.
LONG_NOTES: One or more notes are longer than 127 rows.
DELAY: The delay device is used.
PANNING: One or more tracks have non-center panning.
INDEXDECAY: Some instrument has IndexDecay different from 0.
GAIN: Some instrument has Gain different from 1.
FEEDBACK
I am always very interested in hearing about your adventures with Clinkster,
and to help out if you encounter problems.
Send stories, comments, bug reports and questions to blueberry@loonies.dk or
post them to the Pouet forum at http://pouet.net/prod.php?which=61592
ACKNOWLEDGEMENTS
Thanks to all the people who have tried out this synth (as musician and/or
coder) and given good feedback for its development: Bod, Bstrr, Curt Cool,
Eladamri, El Blanco, Farfar, Garfferen, Hardy, Lemmus, Loaderror, Maytz,
Neoman, Psycho, Punqtured, Response, Seven, TheT, Xerxes, and the ones I
have forgotten.

View File

@ -0,0 +1,665 @@
#!/usr/bin/env python
import sys
import zipfile
import XML
import struct
import ctypes
import math
import datetime
class InputException(Exception):
def __init__(self, message):
Exception.__init__(self)
self.message = message
class Volume:
def __init__(self, left, right):
self.left = left
self.right = right
def __mul__(self, other):
return Volume(self.left * other.left, self.right * other.right)
def isPanned(self):
return self.left != self.right
def makeVolume(xvolume):
v = float(xvolume)
return Volume(v,v)
def makePanning(xpanning):
p = float(xpanning)
return Volume(math.sqrt(2.0 * (1.0 - p)), math.sqrt(2.0 * p))
class Instrument:
NAMES = ["bwave","mwave","bdetune","mdetune",
"bpitchs","bpitchd","mpitchs","mpitchd",
"index","indexspr","indexd","layers","randomseed",
"attack","decay","sustain","release","gain"]
QUAN = [(6,0),(6,0),(101,0),(101,0),
(241,120),(201,128),(241,120),(201,128),
(101,0),(101,0),(201,128),(51,0),(128,0),
(201,128),(201,128),(65,32),(201,128),(201,80)]
def __init__(self, number, name, params):
names,quan = Instrument.NAMES,Instrument.QUAN
self.number = number
self.name = name
self.params = params
self.param_data = [int(p * (quan[i][0]-1) + 0.5) - quan[i][1] for i,p in enumerate(params)]
for i,p in enumerate(self.param_data):
self.__dict__[names[i]] = p
for mp,m in [("layers", 1), ("attack", -120), ("decay", -120), ("release", -120)]:
if self.__dict__[mp] < m:
self.__dict__[mp] = m
print " * Instrument '%s': %s clamped to %d" % (name, mp, m)
self.chopped = self.sustain == 0
self.volume = Volume(1.0, 1.0)
class Note:
NOTEBASES = {
"C": 0, "D": 2, "E": 4, "F": 5, "G": 7, "A": 9, "B": 11
}
NOTENAMES = {
0: "C-", 1: "C#", 2: "D-", 3: "D#", 4: "E-", 5: "F-",
6: "F#", 7: "G-", 8: "G#", 9: "A-", 10: "A#", 11: "B-"
}
def __init__(self, line, songpos, pat, note, instr, velocity):
note = str(note)
self.line = int(line)
self.songpos = int(songpos)
self.pat = int(pat)
self.velocity = 127 if str(velocity) == "" or str(velocity) == ".." else int(str(velocity), 16)
if note == "OFF":
self.off = True
self.tone = None
self.instr = 0
else:
self.off = False
octave = int(note[2])
notebase = Note.NOTEBASES[note[0]]
sharp = int(note[1] == "#")
self.tone = octave * 12 + notebase + sharp
self.instr = int(str(instr), 16)
def instplugins(xinst):
xplugins = xinst.PluginProperties
if xplugins:
return xplugins
return xinst.PluginGenerator
def isactive(xdevice):
if not xdevice:
return False
if xdevice.IsActive.Value:
return float(xdevice.IsActive.Value) != 0.0
else:
return str(xdevice.IsActive) == "true"
def notename(tone):
return Note.NOTENAMES[tone%12] + str(tone/12)
def multibyte(v):
return [-1 - (v >> 8), v & 255] if v > 127 else [v]
class Track:
def __init__(self, number, name, notes, volume, instr, instruments):
self.number = number
self.name = name
self.notes = notes
self.volume = volume * instruments[instr].volume
self.instr = instr
self.notemap = dict()
self.tal_repr = dict()
prev = None
for note in notes:
if prev is not None and not prev.off and prev.instr == instr:
length = 1 if instruments[self.instr].chopped else note.line - prev.line
if length < 0:
raise InputException("Track '%s' has reversed note order from %d to %d" % (name, prev.line, note.line))
if prev.tone is None:
raise InputException("Track '%s' has a toneless note at %d" % (name, prev.line))
tal = (prev.tone, length, prev.velocity)
self.notemap[prev] = tal
prev = note
if not prev.off and prev.instr == instr:
if instr.chopped:
tal = (prev.tone, 1)
self.notemap[prev] = tal
elif not prev.off:
raise InputException("Track '%s' is not terminated." % name)
self.tals = sorted(set(self.notemap.values()), key = (lambda (t,l,v) : (t,v,-l)))
for i,tal in enumerate(self.tals):
if tal[0] is None:
raise InputException("Track '%s' has a toneless note" % name)
self.tal_repr[tal] = i
self.longest_sample = None
self.sample_length_sum = None
class Music:
def __init__(self, version, tracks, instruments, length, ticklength, n_delay_tracks, delay_lengths, delay_strength, master_volume):
self.version = version
self.tracks = tracks
self.instruments = instruments
self.length = length
self.ticklength = ticklength
self.n_delay_tracks = n_delay_tracks
self.delay_lengths = delay_lengths
self.delay_strength = delay_strength
self.master_volume = master_volume
self.uses_waveform = [False] * 6
for t in self.tracks:
inst = self.instruments[t.instr]
self.uses_waveform[inst.bwave] = True
self.uses_waveform[inst.mwave] = True
self.uses_velocity = any(any(tal[2] != 127 for tal in t.tals) for t in self.tracks)
self.uses_long_notes = any(any(tal[1] > 127 for tal in t.tals) for t in self.tracks)
self.uses_delay = (self.n_delay_tracks > 0)
self.uses_panning = any(t.volume.isPanned() for t in self.tracks)
self.uses_indexdecay = any(self.instruments[t.instr].indexd != 0 for t in self.tracks)
self.uses_gain = any(self.instruments[t.instr].gain != 0 for t in self.tracks)
# Calculate longest sample
self.max_longest_sample = 0.0
self.max_sample_length_sum = 0.0
self.max_release_tail = 0.0
for ti,track in enumerate(self.tracks):
track.longest_sample = 0.0
track.sample_length_sum = 0.0
track.max_release_tail = 0.0
def envelope(v):
return pow(2.0, v * 0.125) * 32767.0 / (44100.0 * 4)
instr = instruments[track.instr]
attack_length = envelope(instr.attack)
decay_length = envelope(instr.decay)
release_length = envelope(instr.release)
for tal in track.tals:
note_length = tal[1] * ticklength
sustain_length = max(note_length, attack_length + decay_length)
sample_length = sustain_length + release_length + 32767.0 / (44100.0 * 4) + 0.01
release_tail = sample_length - note_length
track.longest_sample = max(track.longest_sample, sample_length)
track.sample_length_sum += sample_length
track.max_release_tail = max(track.max_release_tail, release_tail)
self.max_longest_sample = max(self.max_longest_sample, track.longest_sample)
self.max_sample_length_sum = max(self.max_sample_length_sum, track.sample_length_sum)
self.max_release_tail = max(self.max_release_tail, track.max_release_tail)
# Track title, specifying resulting track index, track name and instrument number / name
instr = self.instruments[track.instr]
track.title = "%02d: %s / %02X|%s" % (ti, track.name, instr.number, instr.name)
self.datainit = None
self.out = None
def dataline(self, data):
if len(data) > 0:
line = self.datainit
first = True
for d in data:
if not first:
line += ","
line += str(d)
first = False
line += "\n"
self.out += line
def instrparams(self, inst, fields):
return [inst.__dict__[f] if f in inst.__dict__ else f for f in fields]
def comment(self, c):
self.out += "\t; %s\n" % c
def notelist(self, datafunc, trackterm):
for t in self.tracks:
self.comment(t.title)
prev_n = None
pat_data = []
for n in [note for note in t.notes if not note.off and note.instr == t.instr]:
if prev_n is None or n.songpos != prev_n.songpos:
self.dataline(pat_data)
pat_data = []
self.comment("position %d - pattern %d" % (n.songpos, n.pat))
pat_data += datafunc(t,prev_n,n)
prev_n = n
self.dataline(pat_data)
self.dataline(trackterm)
self.out += "\n"
def notebitmask(self):
for t in self.tracks:
self.comment(t.name)
prev_n = None
pat_data = []
pos = 0
data_byte = 0
dummy_note = Note(self.length, 0, 0, "C-0", 0, 127)
for n in [note for note in t.notes if not note.off and note.instr == t.instr] + [dummy_note]:
while pos <= n.line:
data_byte = (data_byte << 1) + (1 if pos == n.line else 0)
pos += 1
if (pos & 7) == 0:
if prev_n is None or n.songpos != prev_n.songpos:
self.dataline(pat_data)
pat_data = []
self.comment("position %d - pattern %d" % (n.songpos, n.pat))
prev_n = n
pat_data.append(data_byte)
data_byte = 0
prev_n = n
self.dataline(pat_data)
self.out += "\n"
def posdata(self, t, pn, n):
step = n.line-pn.line if pn is not None else n.line
return multibyte(step)
def samdata(self, t, pn, n):
return [t.tal_repr[t.notemap[n]]]
def exportPC(self, sample_rate):
self.datainit = "\tdb\t"
self.out = ""
sspt = int(self.ticklength * sample_rate)*4
def roundup(v):
return (int(v) & -0x10000) + 0x10000
feature_flags = self.uses_waveform + [self.uses_velocity, self.uses_long_notes, self.uses_delay, self.uses_panning, self.uses_indexdecay, self.uses_gain]
feature_names = ["SINE", "SAWTOOTH", "SQUARE", "PARABOLA", "TRIANGLE", "NOISE",
"VELOCITY", "LONG_NOTES", "DELAY", "PANNING", "INDEXDECAY", "GAIN"]
print "Features used: " + " ".join(n for f,n in zip(feature_flags, feature_names) if f)
print
global infile
self.out += "; Clinkster music converted from %s %s\n" % (infile, str(datetime.datetime.now())[:-7])
self.out += "\n"
for f,fname in zip(feature_flags, feature_names):
self.out += "%%define USES_%s %d\n" % (fname, int(f))
self.out += "\n"
self.out += "%%define SUBSAMPLES_PER_TICK %d\n" % sspt
self.out += "%%define MAX_INSTRUMENT_SUBSAMPLES %d\n" % roundup((self.max_longest_sample + self.max_release_tail) * (sample_rate * 4.0))
self.out += "%%define MAX_TOTAL_INSTRUMENT_SAMPLES %d\n" % roundup(self.max_sample_length_sum * sample_rate)
self.out += "%%define MAX_RELEASE_SUBSAMPLES %d\n" % roundup(self.max_release_tail * (sample_rate * 4.0))
self.out += "%%define TOTAL_SAMPLES %d\n" % roundup((self.length * self.ticklength + self.max_release_tail) * sample_rate)
self.out += "%%define MAX_TRACK_INSTRUMENT_RENDERS %d\n" % max(len(t.tals) for t in self.tracks)
self.out += "\n"
self.out += "%%define MAX_DELAY_LENGTH %d\n" % int(max(self.delay_lengths) * sample_rate)
self.out += "%%define LEFT_DELAY_LENGTH %d\n" % int(self.delay_lengths[0] * sample_rate)
self.out += "%%define RIGHT_DELAY_LENGTH %d\n" % int(self.delay_lengths[1] * sample_rate)
self.out += "%%define DELAY_STRENGTH %0.8f\n" % self.delay_strength
self.out += "\n"
self.out += "%%define NUMTRACKS %d\n" % len(self.tracks)
self.out += "%%define LOGNUMTICKS %d\n" % int(math.log(self.length, 2) + 1)
self.out += "%%define MUSIC_LENGTH %d\n" % self.length
self.out += "%%define TICKS_PER_SECOND %0.8f\n" % (1.0 / self.ticklength)
# Remap used instruments
wmap = []
j = 0
for i in range(6):
wmap.append(j)
if self.uses_waveform[i]:
j += 1
for inst in self.instruments:
if inst is not None:
for wave in ["bwave", "mwave"]:
inst.__dict__[wave] = wmap[inst.__dict__[wave]]
# Instrument data
self.out += "\n\n\tsection instdata data align=1\n"
self.out += "\n_InstrumentData:\n"
for ti,track in enumerate(self.tracks):
track_volume = track.volume * self.master_volume * makeVolume(32.0)
if self.n_delay_tracks > 0 and ti == self.n_delay_tracks:
self.dataline([-1])
self.comment(track.title)
params = self.instrparams(
self.instruments[track.instr],
["bwave","mwave","bdetune","mdetune",
"indexspr","index","layers","randomseed",
"sustain"] +
([int(track_volume.left), int(track_volume.right)]
if self.uses_panning else
[int(track_volume.left)]) +
["bpitchs","mpitchs","bpitchd","mpitchd"] +
(["indexd"] if self.uses_indexdecay else []) +
(["gain"] if self.uses_gain else []) +
["attack","decay","release"])
self.dataline(params)
# List tones and lengths
taldata = []
prev_t = -1
first = True
for t,l,v in track.tals:
if t > prev_t:
if not first:
taldata += [0]
taldata += [t-prev_t-1]
prev_t = t
if self.uses_velocity:
taldata += [v]
taldata += multibyte(l)
prev_v = v
first = False
taldata += [0,-1]
self.dataline(taldata)
if self.uses_delay:
self.dataline([-1,-1])
else:
self.dataline([-1])
# Positions of notes
self.out += "\n\tsection notepos data align=1\n"
self.out += "\n_NotePositions:\n"
self.notelist(self.posdata, [])
# Samples for notes
self.out += "\n\tsection notesamp data align=1\n"
self.out += "\n_NoteSamples:\n"
self.notelist(self.samdata, [-1])
return self.out
def makeDeltas(self, init_delta, lines_per_beat):
beats_per_line = 1.0/lines_per_beat
deltas = []
for t in self.tracks:
tdeltas = []
delta = init_delta
note_i = 0
for p in range(0, self.length):
while t.notes[note_i].line <= p:
if not t.notes[note_i].off:
delta = p * beats_per_line
note_i += 1
tdeltas.append(delta)
deltas.append(tdeltas)
return deltas
def extractTrackNotes(xsong, tr, col):
outside_pattern = 0
xsequence = xsong.PatternSequence.PatternSequence
if not xsequence:
xsequence = xsong.PatternSequence.SequenceEntries.SequenceEntry
xpatterns = xsong.PatternPool.Patterns.Pattern
tname = str(xsong.Tracks.SequencerTrack[tr].Name)
notes = []
pattern_top = 0
prev_instr = None
for posn,xseq in enumerate(xsequence):
patn = int(xseq.Pattern)
xpat = xpatterns[patn]
nlines = int(xpat.NumberOfLines)
if tr in [int(xmt) for xmt in xseq.MutedTracks.MutedTrack]:
off = Note(pattern_top, posn, patn, "OFF", None, 127)
notes.append(off)
else:
xtrack = xpat.Tracks.PatternTrack[tr]
for xline in xtrack.Lines.Line:
index = int(xline("index"))
if index < nlines:
line = pattern_top + index
xcol = xline.NoteColumns.NoteColumn[col]
if xcol.Note and str(xcol.Note) != "---":
instr = str(xcol.Instrument)
if instr == "..":
if prev_instr is None and str(xcol.Note) != "OFF":
raise InputException("Track '%s' pattern %d position %d: Unspecified instrument" % (tname, patn, index))
instr = prev_instr
prev_instr = instr
note = Note(line, posn, patn, xcol.Note, instr, xcol.Volume)
notes.append(note)
if note.velocity == 0 or note.velocity > 127:
raise InputException("Track '%s' pattern %d position %d: Illegal velocity value" % (tname, patn, index))
# Check for illegal uses of panning, delay and effect columns
def checkColumn(x, msg):
if x and not str(x) in ["", "..", "00"]:
raise InputException("Track '%s' pattern %d position %d: %s" % (tname, patn, index, msg))
checkColumn(xcol.Delay, "Delay column used")
for xeff in xline.EffectColumns.EffectColumn.Number:
checkColumn(xeff, "Effect column used")
else:
outside_pattern += 1
pattern_top += nlines
notes.append(Note(pattern_top, len(xsequence), len(xpatterns), "OFF", 0, 127))
if outside_pattern > 0:
print " * Track '%s': %d note%s outside patterns ignored" % (tname, outside_pattern, "s" * (outside_pattern > 1))
return notes
def pickupDelay(xdevices, delay_lengths, delay_strength, tname, ticklength):
if isactive(xdevices.DelayDevice):
send = float(xdevices.DelayDevice.TrackSend.Value) / 127.0
lfeedback = float(xdevices.DelayDevice.LFeedback.Value)
rfeedback = float(xdevices.DelayDevice.RFeedback.Value)
if float(xdevices.DelayDevice.LineSync.Value):
lsynctime = float(xdevices.DelayDevice.LSyncTime.Value)
lsyncoffset = float(xdevices.DelayDevice.LSyncOffset.Value)
ldelay = (lsynctime + lsyncoffset) * ticklength
rsynctime = float(xdevices.DelayDevice.RSyncTime.Value)
rsyncoffset = float(xdevices.DelayDevice.RSyncOffset.Value)
rdelay = (rsynctime + rsyncoffset) * ticklength
else:
ldelay = float(xdevices.DelayDevice.LDelay.Value) / 1000.0
rdelay = float(xdevices.DelayDevice.RDelay.Value) / 1000.0
if abs(lfeedback - send) > 0.05:
print " * Track '%s': Left feedback (%0.2f) is different from send value (%0.2f)" % (tname, lfeedback, send)
if abs(rfeedback - send) > 0.05:
print " * Track '%s': Right feedback (%0.2f) is different from send value (%0.2f)" % (tname, rfeedback, send)
if delay_lengths != [0.0, 0.0] and ([ldelay,rdelay] != delay_lengths or send != delay_strength):
print " * Track '%s' has different delay parameters from earlier track" % tname
return [ldelay,rdelay],send
return delay_lengths,delay_strength
def makeTracks(version, xsong, ticklength):
instruments = []
delay_tracks = []
non_delay_tracks = []
delay_lengths = [0.0, 0.0]
delay_strength = 0.0
for ii,xinst in enumerate(xsong.Instruments.Instrument):
params = [float(v) for v in instplugins(xinst).PluginDevice.Parameters.Parameter.Value]
if params:
instrument = Instrument(ii, str(xinst.Name), params)
instrument.volume = makeVolume(instplugins(xinst).Volume)
instruments.append(instrument)
else:
instruments.append(None)
for tr,xtrack in enumerate(xsong.Tracks.SequencerTrack):
tname = str(xtrack.Name)
ncols = int(xtrack.NumberOfVisibleNoteColumns)
xdevices = xtrack.FilterDevices.Devices
xdevice = xdevices.SequencerTrackDevice
if not xdevice:
xdevice = xdevices.TrackMixerDevice
volume = makeVolume(xdevice.Volume.Value)
volume *= makePanning(xdevice.Panning.Value)
while isactive(xdevices.SendDevice):
if isactive(xdevices.DelayDevice):
raise InputException("Track '%s' uses both delay and send" % tname);
if str(xdevices.SendDevice.MuteSource) != "true":
raise InputException("Track '%s' uses send without Mute Source" % tname);
volume *= makeVolume(xdevices.SendDevice.SendAmount.Value)
volume *= makePanning(xdevices.SendDevice.SendPan.Value)
dest = int(float(xdevices.SendDevice.DestSendTrack.Value))
xdevices = xsong.Tracks.SequencerSendTrack[dest].FilterDevices.Devices
xdevice = xdevices.SequencerSendTrackDevice
if not xdevice:
xdevice = xdevices.SendTrackMixerDevice
volume *= makeVolume(xdevice.Volume.Value)
volume *= makePanning(xdevice.Panning.Value)
volume *= makeVolume(xdevice.PostVolume.Value)
volume *= makePanning(xdevice.PostPanning.Value)
for col in range(0,ncols):
notes = extractTrackNotes(xsong, tr, col)
track_instrs = []
for note in notes:
if not note.off:
instr = instruments[note.instr]
if instr is None:
raise InputException("Track '%s' uses undefined instrument (%d)" % (tname, note.instr));
if note.instr not in track_instrs:
track_instrs.append(note.instr)
for instr in track_instrs:
track = Track(tr, tname, notes, volume, instr, instruments)
if isactive(xdevices.DelayDevice):
delay_tracks.append(track)
else:
non_delay_tracks.append(track)
delay_lengths,delay_strength = pickupDelay(xdevices, delay_lengths, delay_strength, tname, ticklength)
for xtrack in xsong.Tracks.SequencerSendTrack:
xdevices = xtrack.FilterDevices.Devices
if xdevices.DelayDevice:
delay_lengths,delay_strength = pickupDelay(xdevices, delay_lengths, delay_strength, tname, ticklength)
#delay_tracks = sorted(delay_tracks, key = (lambda t : t.instr))
#non_delay_tracks = sorted(non_delay_tracks, key = (lambda t : t.instr))
return (delay_tracks + non_delay_tracks), len(delay_tracks), delay_lengths, delay_strength, instruments
def makeMusic(xsong):
vstnames = set(str(v) for v in instplugins(xsong.Instruments.Instrument).PluginDevice.PluginIdentifier)
if len(vstnames) > 1:
raise InputException("More than one VST used: %s" % list(vstnames))
vstname = list(vstnames)[0]
vstmap = { "Clinkster" : 1 }
if vstname not in vstmap:
raise InputException("Unknown VST used: %s" % vstname)
vstversion = vstmap[vstname]
print "VST version: %d" % vstversion
if vstversion != 1:
raise InputException("Only Clinkster version 1 supported")
xgsd = xsong.GlobalSongData
if xgsd.PlaybackEngineVersion and int(xgsd.PlaybackEngineVersion) >= 4:
lines_per_minute = float(xgsd.BeatsPerMin) * float(xgsd.LinesPerBeat)
print "New timing format: %d ticks per minute" % lines_per_minute
else:
lines_per_minute = float(xgsd.BeatsPerMin) * 24.0 / float(xgsd.TicksPerLine)
print "Old timing format: %d ticks per minute" % lines_per_minute
ticklength = 60.0 / lines_per_minute
print
tracks,n_delay_tracks,delay_lengths,delay_strength,instruments = makeTracks(vstversion, xsong, ticklength)
xpositions = xsong.PatternSequence.PatternSequence.Pattern
if not xpositions:
xpositions = xsong.PatternSequence.SequenceEntries.SequenceEntry.Pattern
xpatterns = xsong.PatternPool.Patterns.Pattern
length = 0
for xpos in xpositions:
patn = int(xpos)
xpat = xpatterns[patn]
nlines = int(xpat.NumberOfLines)
length += nlines
xmstdev = xsong.Tracks.SequencerMasterTrack.FilterDevices.Devices.SequencerMasterTrackDevice
if not xmstdev:
xmstdev = xsong.Tracks.SequencerMasterTrack.FilterDevices.Devices.MasterTrackMixerDevice
master_volume = makeVolume(xmstdev.Volume.Value)
master_volume *= makePanning(xmstdev.Panning.Value)
master_volume *= makeVolume(xmstdev.PostVolume.Value)
master_volume *= makePanning(xmstdev.PostPanning.Value)
return Music(vstversion, tracks, instruments, length, ticklength, n_delay_tracks, delay_lengths, delay_strength, master_volume)
def printMusicStats(music):
print "Music length: %d ticks at %0.2f ticks per minute" % (music.length, 60.0 / music.ticklength)
print
for ti,track in enumerate(music.tracks):
tnotes = ""
for t,l,v in track.tals:
num_notes = 0
for n in [note for note in track.notes if not note.off and note.instr == track.instr]:
if track.notemap[n] == (t,l,v):
num_notes += 1
if v < 127:
tnotes += " %s/%02X:%d(%d)" % (notename(t), v, l, num_notes)
else:
tnotes += " %s:%d(%d)" % (notename(t), l, num_notes)
if music.n_delay_tracks > 0 and ti == 0:
print "Tracks with delay:"
print
if music.n_delay_tracks > 0 and ti == music.n_delay_tracks:
print
print "Tracks without delay:"
print
print track.title
print tnotes
#print "Max: longest %f, total %f" % (music.max_longest_sample, music.max_sample_length_sum)
def writefile(filename, s):
f = open(filename, "wb")
f.write(s)
f.close()
print "Wrote file %s" % filename
if len(sys.argv) < 3:
print "Usage: %s <input xrns file> <output asm file>" % sys.argv[0]
sys.exit(1)
infile = sys.argv[1]
outfile = sys.argv[2]
x = XML.makeXML(zipfile.ZipFile(infile).read("Song.xml"))
try:
music = makeMusic(x.RenoiseSong)
print
printMusicStats(music)
print
writefile(outfile, music.exportPC(44100.0))
if len(sys.argv) > 3:
deltas = music.makeDeltas(0.0, 1.0)
syncfile = sys.argv[3]
header = ""
header += struct.pack('I', 1)
header += struct.pack('I', music.length*4)
header += struct.pack('I', len(music.tracks)*music.length*4)
body = ""
for t,tdeltas in enumerate(deltas):
body += struct.pack("%df" % len(tdeltas), *tdeltas)
data = header + body
writefile(syncfile, data)
except InputException, e:
print "Error in input song: %s" % e.message

View File

@ -0,0 +1,91 @@
#!/usr/bin/env python
import xml.dom
import xml.dom.minidom
class XML(object):
def __init__(self, domlist):
self.domlist = list(domlist)
def __getattr__(self, name):
l = []
for d in self.domlist:
for c in d.childNodes:
if c.nodeName == name:
l.append(c)
return XML(l)
def __len__(self):
return len(self.domlist)
def __getitem__(self, i):
if i >= len(self.domlist):
return XML([])
return XML([self.domlist[i]])
def __iter__(self):
for d in self.domlist:
yield XML([d])
def __call__(self, attrname):
s = ""
for d in self.domlist:
if d.nodeType == xml.dom.Node.ELEMENT_NODE and d.hasAttribute(attrname):
s += d.getAttribute(attrname)
return s
def __str__(self):
def collect(dl):
s = ""
for d in dl:
if d.nodeType == xml.dom.Node.TEXT_NODE:
s += d.data
else:
s += collect(d.childNodes)
return s
return collect(self.domlist)
def __int__(self):
return int(str(self))
def __float__(self):
return float(str(self))
def __nonzero__(self):
return len(self.domlist) != 0
def replaceText(self, fun):
def collect(dl):
for d in dl:
if d.nodeType == xml.dom.Node.TEXT_NODE:
d.data = fun(d.data)
else:
collect(d.childNodes)
collect(self.domlist)
def setData(self, data):
sdata = str(data)
for d in self.domlist:
for c in d.childNodes:
c.data = sdata
def removeChild(self, child):
if len(self.domlist) != len(child.domlist):
raise ValueError
for p,c in zip(self.domlist, child.domlist):
p.removeChild(c)
def insertBefore(self, newChild, refChild):
if len(self.domlist) != len(newChild.domlist) or len(newChild.domlist) != len(refChild.domlist):
raise ValueError
for p,nc,rc in zip(self.domlist, newChild.domlist, refChild.domlist):
p.insertBefore(nc.childNodes[0],rc)
def export(self):
return "".join(x.toxml("utf-8") for x in self.domlist)
def readXML(filename):
return XML([xml.dom.minidom.parse(filename)])
def makeXML(xstring):
return XML([xml.dom.minidom.parseString(xstring)])

Binary file not shown.

View File

@ -0,0 +1,9 @@
del /q temp\*
del music.exe
tools\RenoiseConvert.exe music.xrns temp\music.asm
tools\nasmw -f win32 src\clinkster_multithreaded.asm -o temp\clinkster_multithreaded.obj
tools\nasmw -f win32 src\play.asm -o temp\play.obj
tools\crinkler20\crinkler temp\clinkster_multithreaded.obj temp\play.obj /OUT:music.exe /ENTRY:main tools\kernel32.lib tools\user32.lib tools\winmm.lib tools\msvcrt_old.lib @crinkler_options.txt
pause

View File

@ -0,0 +1,9 @@
del /q temp\*
del music_wav.exe
tools\RenoiseConvert.exe music.xrns temp\music.asm
tools\nasmw -f win32 src\clinkster_multithreaded.asm -o temp\clinkster_multithreaded.obj
tools\nasmw -f win32 -dWRITE_WAV src\play.asm -o temp\play.obj
tools\crinkler20\crinkler temp\clinkster_multithreaded.obj temp\play.obj /OUT:music_wav.exe /ENTRY:main tools\kernel32.lib tools\user32.lib tools\winmm.lib tools\msvcrt_old.lib @crinkler_options.txt
pause

View File

@ -0,0 +1 @@
/UNSAFEIMPORT /COMPMODE:INSTANT /HASHSIZE:100

View File

@ -0,0 +1,4 @@
Music composed using 4k synth "Clinkster" by Blueberry / Loonies
Generating music...

Binary file not shown.

View File

@ -0,0 +1,16 @@
This setup is for easily building an executable version of a piece of music
created using Clinkster.
Proceed as follows:
1. Place your music here, named music.xrns.
2. Place a text file containing the text you would like the executable to
print at startup, named music.txt.
3. Optionally modify the Crinkler options in crinkler_options.txt
(read the Crinkler manual for details).
4. Run build.bat to get an executable that plays the music, or
build_wav.bat to get one that writes the music in WAV format to
music.wav and then plays it.
Enjoy!

View File

@ -0,0 +1,12 @@
extern Clinkster_GenerateMusic
extern Clinkster_StartMusic
extern Clinkster_GetPosition
extern Clinkster_GetInstrumentTrigger
extern Clinkster_MusicBuffer
extern Clinkster_NoteTiming
extern Clinkster_TicksPerSecond
extern Clinkster_MusicLength
extern Clinkster_NumTracks
extern Clinkster_WavFileHeader

View File

@ -0,0 +1,962 @@
; If set to 1, timing information is generated during music generation
; which is needed for Clinkster_GetInstrumentTrigger.
; Set it to 0 if you don't need this functionality.
%define CLINKSTER_GENERATE_TIMING_DATA 0
; Offset applied by Clinkster_GetPosition to compensate for graphics latency.
; Measured in samples (44100ths of a second).
; The default value of 2048 (corresponding to about 46 milliseconds) is
; appropriate for typical display latencies for high-framerate effects.
%define CLINKSTER_TIMER_OFFSET 0
%include "temp/music.asm"
;; ********** Definitions **********
global Clinkster_GenerateMusic
global _Clinkster_GenerateMusic@0
global Clinkster_StartMusic
global _Clinkster_StartMusic@0
global Clinkster_GetPosition
global _Clinkster_GetPosition@0
global Clinkster_GetInstrumentTrigger
global _Clinkster_GetInstrumentTrigger@8
global Clinkster_MusicBuffer
global _Clinkster_MusicBuffer
global Clinkster_TicksPerSecond
global _Clinkster_TicksPerSecond
global Clinkster_MusicLength
global _Clinkster_MusicLength
global Clinkster_NumTracks
global _Clinkster_NumTracks
%if CLINKSTER_GENERATE_TIMING_DATA
global Clinkster_NoteTiming
global _Clinkster_NoteTiming
%endif
global Clinkster_WavFileHeader
global _Clinkster_WavFileHeader
extern __imp__waveOutOpen@24
extern __imp__waveOutPrepareHeader@12
extern __imp__waveOutWrite@12
extern __imp__waveOutGetPosition@12
extern __imp__CreateThread@24
extern __imp__WaitForSingleObject@8
%define SAMPLE_RATE 44100
%define WAVE_SIZE 65536
;; ********** Public variables **********
section MusBuf bss align=4
Clinkster_MusicBuffer:
_Clinkster_MusicBuffer:
.align24
resw (TOTAL_SAMPLES*2)
resw 2 ; padding to catch extra write in conversion
section tps rdata align=4
Clinkster_TicksPerSecond:
_Clinkster_TicksPerSecond:
dd TICKS_PER_SECOND
section muslen rdata align=4
Clinkster_MusicLength:
_Clinkster_MusicLength:
dd MUSIC_LENGTH
section numtr rdata align=4
Clinkster_NumTracks:
_Clinkster_NumTracks:
dd NUMTRACKS
%if CLINKSTER_GENERATE_TIMING_DATA
section musdat bss align=4
Clinkster_NoteTiming:
_Clinkster_NoteTiming:
.align16
resd 2*(NUMTRACKS<<LOGNUMTICKS)
section timing data align=4
timing_ptr: dd Clinkster_NoteTiming
%endif
section WavFile rdata align=4
Clinkster_WavFileHeader:
_Clinkster_WavFileHeader:
db "RIFF"
dd 36+TOTAL_SAMPLES*4
db "WAVE"
db "fmt "
dd 16
dw 1,2
dd SAMPLE_RATE
dd SAMPLE_RATE*4
dw 4,16
db "data"
dd TOTAL_SAMPLES*4
;; ********** System structures **********
section WaveForm rdata align=1
_WaveFormat:
dw 1,2
dd SAMPLE_RATE
dd SAMPLE_RATE*4
dw 4,16,0
section WaveHdr data align=4
_WaveHdr:
dd Clinkster_MusicBuffer
dd (TOTAL_SAMPLES*4)
dd 0,0,0,0,0,0
section wavehand bss align=4
_WaveOutHandle:
.align16
resd 1
section WaveTime data align=4
_WaveTime:
dd 4,0,0,0,0,0,0,0
;; ********** Internal buffers **********
section wforms bss align=4
waveforms:
.align16
resd 6*WAVE_SIZE
;; ********** Instrument parameter access **********
section paramw rdata align=4
param_weights:
dd 0.125 ; Release
dd 0.125 ; Decay
dd 0.125 ; Attack
%if USES_GAIN
dd 0.125 ; Gain
%endif
%if USES_INDEXDECAY
dd 0.0009765625 ; IndexDecay
%endif
dd 0.0009765625 ; M PitchDecay
dd 0.0009765625 ; B PitchDecay
dd 0.083333333333 ; M Pitch
dd 0.083333333333 ; B Pitch
dd 0.0000152587890625 ; Volume
%if USES_PANNING
dd 0.0000152587890625 ; Volume
%endif
dd 0.03125 ; Sustain
dd 16307 ; RandomSeed
dd 1 ; Layers
dd 4096.0 ; Index
dd 0.125 ; Index Spread
dd 0.0009765625 ; M Detune
dd 0.0009765625 ; B Detune
dd 65536 ; ModWave
dd 65536 ; BaseWave
struc instr_params
ip_basewave: resd 1
ip_modwave: resd 1
ip_bdetune: resd 1
ip_mdetune: resd 1
ip_indexspr: resd 1
ip_index: resd 1
ip_layers: resd 1
ip_randomseed: resd 1
ip_sustain: resd 1
ip_volume: resd 1+USES_PANNING
ip_bpitch: resd 1
ip_mpitch: resd 1
ip_bpitchd: resd 1
ip_mpitchd: resd 1
%if USES_INDEXDECAY
ip_indexd: resd 1
%endif
%if USES_GAIN
ip_gain: resd 1
%endif
ip_attack: resd 1
ip_decay: resd 1
ip_release: resd 1
endstruc
%define ip_INT 0
%define ip_FLOAT 0
%define IP(f,t) dword [dword ebx + g_instrparams + ip_ %+ f + ip_ %+ t]
%define IPI(f,i,t) dword [dword ebx + g_instrparams + ip_ %+ f + ip_ %+ t + i]
;; ********** Internal constants and tables **********
section resamp rdata align=4
resamplefilter:
db -1,-2,-4,-4,-2,3,14,30,51,98,116,126
db 126,116,98,51,30,14,3,-2,-4,-4,-2,-1
resamplefilter_end:
FILTER_SIZE equ (resamplefilter_end-resamplefilter)
section wavestep rdata align=4
c_wavestep: dd 0.000030517578125
section basefreq rdata align=4
c_basefreq: dd 2.86698696365342
section halfnote rdata align=4
c_halfnote: dd 1.05946309435929
section finalamp rdata align=4
c_finalamp: dd 32767
section velfac rdata align=4
c_velocityfac: dd 0.007874015748031496
section delaystr rdata align=4
c_delaystr: dd DELAY_STRENGTH
section offset rdata align=4
c_timeoffset: dd CLINKSTER_TIMER_OFFSET*4
section tempo rdata align=4
c_ticklength: dd SUBSAMPLES_PER_TICK/4*4
section half rdata align=4
c_onehalf: dd 0.5
;; ********** Internal global variables **********
struc globalvars
g_phasetemp: resd 1
g_layer_random: resd 1
g_stereo: resd 1 ; 0 for left channel, 2 for right channel
g_noteposptr: resd 1
g_notesamptr: resd 1
g_instrparams: resb instr_params_size
g_layerparams: resq 0
g_layer_bfreq: resq 1
g_layer_mfreq: resq 1
g_layer_index: resq 1
g_layer_bpitch: resq 1
g_layer_mpitch: resq 1
g_layer_bpitchd: resq 1
g_layer_mpitchd: resq 1
%if USES_INDEXDECAY
g_layer_indexd: resq 1
%endif
%if USES_GAIN
g_layer_gain: resq 1
%endif
g_layer_attack: resq 1
g_layer_decay: resq 1
g_layer_release: resq 1
alignb 256
g_InstrumentPointers:
resd MAX_TRACK_INSTRUMENT_RENDERS+1
resd MAX_DELAY_LENGTH
alignb 16777216
g_MixingBuffer:
resd TOTAL_SAMPLES
alignb 16777216
g_InstrumentBuffer:
resd MAX_INSTRUMENT_SUBSAMPLES
resd 256
alignb 16777216
g_InstrumentRender:
resd MAX_INSTRUMENT_SUBSAMPLES
alignb 16777216
g_InstrumentStore:
resd MAX_TOTAL_INSTRUMENT_SAMPLES
endstruc
section vars bss align=8
vars_align16
globals:
resb globalvars_size
resb globalvars_size
;; ********** Generate the sound for one layer **********
section mklayer text align=1
makelayer:
lea edx, [dword ebx + g_layerparams]
; Init random variables for layer
fild word [dword ebx + g_layer_random]
mov ecx, dword [dword ebx + g_layer_random]
ror ecx, cl
dec ecx
mov dword [dword ebx + g_layer_random], ecx
fld IP(bdetune, FLOAT)
fmul st0, st0
fmulp st1, st0
fadd st0, st1
fstp qword [edx]
add edx, byte 8
fild word [dword ebx + g_layer_random]
mov ecx, dword [dword ebx + g_layer_random]
ror ecx, cl
dec ecx
mov dword [dword ebx + g_layer_random], ecx
fld IP(mdetune, FLOAT)
fmul st0, st0
fmulp st1, st0
fadd st0, st1
fstp qword [edx]
add edx, byte 8
fild word [dword ebx + g_layer_random]
mov ecx, dword [dword ebx + g_layer_random]
ror ecx, cl
dec ecx
mov dword [dword ebx + g_layer_random], ecx
fmul IP(indexspr, FLOAT)
fadd IP(index, FLOAT)
fstp qword [edx]
add edx, byte 8
; Init exponentiated variables for layer
lea edi, [dword ebx + g_instrparams+ip_bpitch]
mov ecx, 7+USES_INDEXDECAY+USES_GAIN
.powloop:
fld dword [edi]
fld1
fld st1
fprem
fstp st1
f2xm1
fld1
faddp st1, st0
fscale
fstp qword [edx]
add edx, byte 8
fstp st0
scasd
loop .powloop
; Loop over samples
fldz ; b phase
fldz ; m phase
lea edi, [dword ebx + g_InstrumentBuffer]
; Calculate max note size
xor eax, eax
%if USES_VELOCITY
lodsb ; Skip velocity
%endif
%if USES_LONG_NOTES
cmp [esi], byte 0
jge near .short_notelen
lodsb
not al
shl eax, 8
.short_notelen:
%endif
lodsb ; Length of longest note with this tone
mov edx, SUBSAMPLES_PER_TICK
mul edx
add eax, MAX_RELEASE_SUBSAMPLES
xchg ecx, eax
.sampleloop:
lea edx, [dword ebx + g_layerparams]
; Look up and normalize mod wave
fist dword [ebx]
mov eax, IP(modwave,INT)
mov ax, [ebx]
fld dword [waveforms + eax*4]
; Adjust by index
fmul qword [edx + 2*8] ; layer_index
fadd st0, st2
; Look up base wave
fistp dword [ebx]
mov eax, IP(basewave,INT)
mov ax, [ebx]
fld dword [waveforms + eax*4]
; Update phases
fld qword [edx] ; layer_bfreq
add edx, byte 8
fmul qword [edx + 2*8] ; layer_bpitch
faddp st3, st0
fld qword [edx] ; layer_mfreq
add edx, byte 8
fmul qword [edx + 2*8] ; layer_mpitch
faddp st2, st0
%if USES_INDEXDECAY
fld qword [edx] ; layer_index
fld1
fadd st1, st0
fsubp st1, st0
%endif
add edx, byte 8
; Update pitches: p := (p-1)*d+1
.update:
fld1
fld qword [edx] ; layer_(b/m)pitch
fsub st0, st1
fmul qword [edx + 2*8] ; layer_(b/m)pitchd
faddp st1, st0
fstp qword [edx] ; layer_(b/m)pitch
add edx, byte 8
neg ecx
js .update
%if USES_INDEXDECAY
fmul qword [edx + 2*8] ; layer_indexd
fstp qword [edx - 3*8] ; layer_index
%endif
; Add to existing layers
fadd dword [edi]
fstp dword [edi]
scasd
loop .sampleloop
fstp st0
fstp st0
ret
;; ********** Interpolate one section of amplitude envelope **********
section adsr text align=1
apply_adsr:
; On condition g:
; st0 = amplitude target
; st1 = amplitude
; st2 = velocity / nlayers
; eax = number of samples
; ecx = sample index
; On condition le:
; st0 = number of samples
; st1 = amplitude target
; st2 = amplitude
; st3 = velocity / nlayers
; ecx = sample index
push eax
jg .integer_length
fimul dword [c_finalamp]
fistp dword [esp]
.integer_length:
fsub st0, st1
fild dword [esp]
pop eax
add eax, ecx
fdivp st1, st0
.adsrloop:
fld dword [dword ebx + g_InstrumentBuffer + ecx*4]
fmul st0, st3 ; velocity / nlayers
fmul st0, st2 ; envelope value
%if USES_GAIN
fld1
fsubr qword [dword ebx + g_layer_gain]
fmul st0, st1
fmul st0, st1
fld1
faddp st1, st0
fdivr qword [dword ebx + g_layer_gain]
fsqrt
fmulp st1, st0
%endif
fstp dword [dword ebx + g_InstrumentRender + ecx*4]
fadd st1, st0
inc ecx
cmp ecx, eax
jl .adsrloop
fstp st0
ret
;; ********** Main music generation **********
section genMus text align=1
Clinkster_GenerateMusic:
_Clinkster_GenerateMusic@0:
pusha
fninit
; Make waveforms
mov edi, waveforms
%if USES_SINE
fldz
mov ecx, WAVE_SIZE
.sineloop:
fadd dword [c_wavestep]
fld st0
fldpi
fmulp st1, st0
fsin
fstp dword [edi]
scasd
loop .sineloop
fstp st0
%endif
%if USES_SAWTOOTH
fld1
fchs
mov ecx, WAVE_SIZE
.sawtoothloop:
fadd dword [c_wavestep]
fst dword [edi]
scasd
loop .sawtoothloop
fstp st0
%endif
%if USES_SQUARE
fld1
fchs
mov ecx, WAVE_SIZE
.squareloop:
cmp ecx, WAVE_SIZE/2
jne .notflipsq
fabs
.notflipsq:
fst dword [edi]
scasd
loop .squareloop
fstp st0
%endif
%if USES_PARABOLA
fld1
fchs
mov ecx, WAVE_SIZE
.parabolaloop:
fadd dword [c_wavestep]
fld st0
fmul st0, st1
fadd st0, st0
fld1
fsubp st1, st0
fstp dword [edi]
scasd
loop .parabolaloop
fstp st0
%endif
%if USES_TRIANGLE
fld1
fchs
mov ecx, WAVE_SIZE
.triangleloop:
fadd dword [c_wavestep]
fld st0
fadd st0, st1
fabs
fld1
fsubp st1, st0
fstp dword [edi]
scasd
loop .triangleloop
fstp st0
%endif
%if USES_NOISE
fld1
fchs
mov ecx, WAVE_SIZE
.noiseloop:
fadd dword [c_wavestep]
fldpi
fmulp st1, st0
fsin
fst dword [edi]
scasd
loop .noiseloop
fstp st0
%endif
push byte 0 ; lpThreadId
push byte 0 ; dwCreationFlags
push byte 0 ; lpParameter
push makechannel
push byte 0 ; dwStackSize
push byte 0 ; lpThreadAttributes
call [__imp__CreateThread@24]
push byte -1
push eax
push byte 2
call makechannel
call [__imp__WaitForSingleObject@8]
popa
ret
makechannel:
; eax = channel (0 or 2)
mov eax, [esp + 4]
mov edx, globalvars_size/2
mul edx
mov ebx, globals
add ebx, eax
mov eax, [esp + 4]
mov [dword ebx + g_stereo], eax
mov dword [dword ebx + g_noteposptr], _NotePositions
mov dword [dword ebx + g_notesamptr], _NoteSamples
mov esi, _InstrumentData
%if USES_DELAY
jmp short .trackloop
.delay:
mov eax, dword [dword ebx + g_stereo]
mov edx, (LEFT_DELAY_LENGTH-RIGHT_DELAY_LENGTH)*4/2
mul edx
sub eax, LEFT_DELAY_LENGTH*4
lea edi, [dword ebx + g_MixingBuffer]
mov ecx, TOTAL_SAMPLES
.delayloop:
fld dword [edi+eax]
fmul dword [c_delaystr]
fadd dword [edi]
fstp dword [edi]
scasd
loop .delayloop
%endif
.trackloop:
; ESI = instr data
lea edi, [dword ebx + g_instrparams]
mov ecx, instr_params_size/4
.ploop:
lodsb
movsx eax, al
push eax
fild dword [esp]
pop eax
fmul dword [param_weights-4+ecx*4]
fstp dword [edi]
scasd
loop .ploop
lea edi, [dword ebx + g_instrparams+ip_bpitchd]
mov ecx, 2+USES_INDEXDECAY
.cubeloop:
fld dword [edi]
fld st0
fmul st0, st0
fmulp st1, st0
fstp dword [edi]
scasd
loop .cubeloop
lea ebp, [dword ebx + g_InstrumentPointers]
lea edi, [dword ebx + g_InstrumentStore]
mov dword [ebp], edi ; store first instrument instance address
fld dword [c_basefreq]
; Loop over instrument tones
.toneloop:
xor eax, eax
lodsb ; Tone
.freqloop:
fmul dword [c_halfnote]
dec eax
jge .freqloop
; random seed for channel = RandomSeed * 16307 + channel * 12042
mov eax, dword [dword ebx + g_stereo]
mov edx, 12042/2
mul edx
add eax, IP(randomseed,INT)
xchg ecx, eax
mov dword [dword ebx + g_layer_random], ecx
xor eax, eax
lea edi, [dword ebx + g_InstrumentBuffer]
mov ecx, MAX_INSTRUMENT_SUBSAMPLES
rep stosd
; Loop over layers
mov ecx, IP(layers,INT)
.layerloop:
pusha
call makelayer
popa
loop .layerloop
.lengthloop:
%if USES_VELOCITY
lodsb
movsx eax, al
push eax
fild dword [esp]
pop eax
fmul dword [c_velocityfac]
%else
fld1
%endif
fidiv IP(layers,INT)
xor ecx, ecx ; sample index
fldz ; amplitude level
fld1 ; attack amplitude target
fld qword [dword ebx + g_layer_attack]; attack length
call apply_adsr
fld IP(sustain,FLOAT) ; decay amplitude target
fld qword [dword ebx + g_layer_decay] ; decay length
call apply_adsr
xor eax, eax
%if USES_LONG_NOTES
cmp [esi], byte 0
jge near .short_notelen
lodsb
not al
shl eax, 8
.short_notelen:
%endif
lodsb ; note length in ticks
mov edx, SUBSAMPLES_PER_TICK
mul edx
sub eax, ecx ; note length exclusing attack and decay
jle .nosustain ; attack + decay overflows note length
fld IP(sustain,FLOAT) ; sustain amplitude target
call apply_adsr
.nosustain:
fldz ; release amplitude target
fld qword [dword ebx + g_layer_release];release length
call apply_adsr
fldz ; padding amplitude
fld1 ; padding length
call apply_adsr
fstp st0
fstp st0
; Resampling
push esi
mov edi, [ebp] ; instrument instance address
add ebp, byte 4
xchg edx, eax
lea esi, [dword ebx + g_InstrumentRender - FILTER_SIZE*4]
.resampleloop:
fldz
mov ecx, FILTER_SIZE
.filterloop:
movsx eax, byte [resamplefilter + ecx - 1]
push eax
fild dword [esp]
pop eax
fmul dword [esi + ecx*4]
faddp st1, st0
loop .filterloop
%if USES_PANNING
mov eax, dword [dword ebx + g_stereo]
fmul IPI(volume,eax*2,FLOAT)
%else
fmul IP(volume,FLOAT)
%endif
fstp dword [edi]
scasd
add esi, byte 4*4
sub edx, byte 4
jg .resampleloop
mov [ebp], edi ; store instrument instance address
pop esi
cmp [esi], byte 0
jne near .lengthloop
lodsb
cmp [esi], byte 0
jge near .toneloop
lodsb
fstp st0
; Mixing
lea ebp, [dword ebx + g_MixingBuffer]
xchg esi, dword [dword ebx + g_notesamptr]
.noteloop:
xchg esi, dword [dword ebx + g_noteposptr]
xor eax, eax
cmp [esi], byte 0
jge near .short_notepos
lodsb
not al
shl eax, 8
.short_notepos:
lodsb
mov edx, SUBSAMPLES_PER_TICK/4*4
mul edx
add ebp, eax
%if CLINKSTER_GENERATE_TIMING_DATA
mov ecx, SUBSAMPLES_PER_TICK/4*4
div ecx
xchg edx, eax
mov edi, [timing_ptr]
mov eax, [edi]
mov ecx, edx
rep stosd
mov [timing_ptr], edi
add eax, edx
stosd
%endif
xchg esi, dword [dword ebx + g_noteposptr]
xor eax, eax
lodsb
mov edx, dword [dword ebx + g_InstrumentPointers + eax*4] ; Instrument instance ptr
mov edi, ebp
.mixloop:
fld dword [edx]
fadd dword [edi]
fstp dword [edi]
scasd
add edx, byte 4
cmp edx, dword [dword ebx + g_InstrumentPointers + eax*4 + 4]
jl .mixloop
cmp [esi], byte 0
jge near .noteloop
lodsb
xchg esi, dword [dword ebx + g_notesamptr]
%if CLINKSTER_GENERATE_TIMING_DATA
mov ecx, 1<<LOGNUMTICKS
mov edi, [timing_ptr]
mov eax, [edi]
sub ecx, eax
rep stosd
mov [timing_ptr], edi
%endif
cmp [esi], byte 0
jge near .trackloop
lodsb
%if USES_DELAY
cmp [esi], byte 0
jge near .delay
%endif
; Clamp and convert to shorts
fld1
mov edi, Clinkster_MusicBuffer
mov ecx, TOTAL_SAMPLES
add edi, dword [dword ebx + g_stereo]
.sloop:
fld dword [dword ebx + g_MixingBuffer + ecx*4]
fcomi st0, st1
fcmovnb st0, st1
fchs
fcomi st0, st1
fcmovnb st0, st1
fchs
fimul dword [c_finalamp]
fistp word [edi + ecx*4]
loop .sloop
fstp st0
ret 4
;; ********** Start music **********
section startmus text align=1
Clinkster_StartMusic:
_Clinkster_StartMusic@0:
; Start music
push byte 0
push byte 0
push byte 0
push _WaveFormat
push byte -1
push _WaveOutHandle
call [__imp__waveOutOpen@24]
push byte 32 ; sizeof(WAVEHDR)
push _WaveHdr
push dword [_WaveOutHandle] ; waveOutHandle
call [__imp__waveOutPrepareHeader@12]
push byte 32 ; sizeof(WAVEHDR)
push _WaveHdr
push dword [_WaveOutHandle]
call [__imp__waveOutWrite@12]
ret
;; ********** Get current play position **********
section getpos text align=1
Clinkster_GetPosition:
_Clinkster_GetPosition@0:
push byte 32 ; sizeof(MMTIME)
push _WaveTime
push dword [_WaveOutHandle]
call [__imp__waveOutGetPosition@12]
fild dword [_WaveTime+4]
%if CLINKSTER_TIMER_OFFSET>0
fiadd dword [c_timeoffset]
%endif
fidiv dword [c_ticklength]
ret
;; ********** Get time since instrument trigger **********
%if CLINKSTER_GENERATE_TIMING_DATA
section insttrig text align=1
Clinkster_GetInstrumentTrigger:
_Clinkster_GetInstrumentTrigger@8:
cvttss2si eax, [esp+8]
mov ecx, [esp+4]
shl ecx, LOGNUMTICKS+2
fld dword [esp+8]
fisub dword [Clinkster_NoteTiming+ecx+eax*4]
ret 8
%endif

View File

@ -0,0 +1,124 @@
%include "src/clinkster.inc"
global _main
extern __imp__fopen
extern __imp__fwrite
extern __imp__fclose
extern __imp__printf
extern __imp__GetAsyncKeyState@4
extern __imp__ExitProcess@4
extern __imp__Sleep@4
section main text align=1
_main:
push message
push messageformat
call [__imp__printf]
add esp, byte 2*4
call Clinkster_GenerateMusic
%ifdef WRITE_WAV
push wavname
push wavformat
call [__imp__printf]
add esp, byte 2*4
push filemode
push wavname
call [__imp__fopen]
add esp, byte 2*4
push eax
push eax
push eax
push byte 44
push byte 1
push Clinkster_WavFileHeader
call [__imp__fwrite]
add esp, byte 3*4
push dword [Clinkster_WavFileHeader+40]
push byte 1
push Clinkster_MusicBuffer
call [__imp__fwrite]
add esp, byte 3*4
call [__imp__fclose]
add esp, byte 1*4
%endif
call Clinkster_StartMusic
.playloop:
mov ebx, 60
fild dword [Clinkster_MusicLength]
fdiv dword [Clinkster_TicksPerSecond]
push eax
fistp dword [esp]
pop eax ; music length in seconds
xor edx, edx
div ebx
push edx
push eax
call Clinkster_GetPosition
fdiv dword [Clinkster_TicksPerSecond]
push eax
fistp dword [esp]
pop eax ; play position in seconds
xor edx, edx
div ebx
push edx
push eax
push timeformat
call [__imp__printf]
add esp, byte 5*4
push byte 100
call [__imp__Sleep@4]
push byte 27
call [__imp__GetAsyncKeyState@4]
test ax, ax
je .playloop
push byte 0
call [__imp__ExitProcess@4]
section mformat rdata align=1
messageformat:
db "%s",0
section wformat rdata align=1
wavformat:
db "Writing music to %s...",10,10,0
section wname rdata align=1
wavname:
db "music.wav",0
section wb rdata align=1
filemode:
db "wb",0
section tformat rdata align=1
timeformat:
db 13,"Playing %d:%02d / %d:%02d",0
section message rdata align=1
message:
incbin "music.txt"
db 0

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

36
clinkster/Clinkster/history.txt vendored Normal file
View File

@ -0,0 +1,36 @@
Change history of Clinkster:
2016-04-15:
- Fixed an error in the multithreaded player code which made it not
compile if panning was used.
- Added a WAV writer feature to the easy_exe setup.
- Added checks for illegal use of command columns to the converter.
- Added music from U-Boat to examples.
2015-10-31:
- Added some code in the player to avoid denormals in the index decay,
speeding up computation of long notes with high index decay.
- Padded the music buffer to avoid writing into adjacent memory.
- Included a multithreaded version of the player, which computes the
left and right channels in separate threads.
- Included a Windows 64-bit build of the VST.
2014-11-30:
- Added converter support for Renoise 3
- Added some checks in the converter for illegal use of the
volume, panning, delay and effect columns
2013-08-18:
- Added music from Wishful Twisting to examples
2013-08-15:
- Fixed bug in converter: sometimes incorrect waveforms would be assigned
to instruments used in more than one track
- Fixed bug in converter: music would break if attack, decay or release
was set to -128 (minimum)
- Fixed bug in player: crash if sine waveform not used
- One more example song
- A bunch of additional example instruments
2013-07-19: First public release

34
clinkster/Clinkster/license.txt vendored Normal file
View File

@ -0,0 +1,34 @@
CLINKSTER - a software synthesizer for 4k intros by Blueberry / Loonies
With the exceptions noted below, all content of this package is Copyright
2008-2016 Aske Simon Christensen. It may be freely used, distributed,
modified, redistributed, studied and laughed at by any person or entity.
ALL CONTENT OF THE PACKAGE COMES WITH NO WARRANTY OF ANY KIND. THE AUTHORS OF
THE CONTENT CAN NOT BE HELD RESPONSIBLE FOR ANY CONSEQUENCES OF THE USE OF THE
CONTENT, EVEN IF YOUR CAPS-LOCK KEY IS STUCK.
If you distribute music made using the synth, on its own or as part of a
composite work, proper attribution is highly appreciated, though not strictly
required.
Exceptions:
- The music examples in examples/songs are Copyright of their respective
authors. They are distributable under the Creative Commons Attribution
license.
- The VST is based on the VST SDK, located in vst_src/source/vst2.x, which is
Copyright Steinberg Media Technologies Gmbh.
- Crinkler, located in easy_exe/tools/crinkler20, is covered by its own
licensing terms, which are included.
- The .lib and .dll files in easy_exe/tools are Copyright Microsoft
Corporation.
- Nasm, located in easy_exe/tools, is distributable under the GNU Lesser
General Public License.

View File

@ -0,0 +1,46 @@
#include "clinkster.h"
#include <stdio.h>
#include <Windows.h>
void printTime(float ticks) {
int sec = (int)(ticks / Clinkster_TicksPerSecond);
printf("%d:%02d", sec / 60, sec % 60);
}
int main(int argc, char **argv)
{
printf("Music composed using 4k synth \"Clinkster\" by Blueberry / Loonies\n\n");
printf("Generating...\n");
Clinkster_GenerateMusic();
printf("Saving...\n");
FILE *outfile = fopen("music.wav", "wb");
fwrite(Clinkster_WavFileHeader, 1, sizeof(Clinkster_WavFileHeader), outfile);
fwrite(Clinkster_MusicBuffer, 1, Clinkster_WavFileHeader[10], outfile);
fclose(outfile);
printf("Playing...\n\n");
Clinkster_StartMusic();
while (!GetAsyncKeyState(VK_ESCAPE)) {
float pos = Clinkster_GetPosition();
if (pos > Clinkster_MusicLength) break;
printf("\r ");
printTime(pos);
printf(" / ");
printTime((float)Clinkster_MusicLength);
printf(" ");
for (int i = 0 ; i < Clinkster_NumTracks ; i++) {
float t = Clinkster_GetInstrumentTrigger(i, pos);
int ti = (int)(t * 0.25);
if (ti > 3) ti = 3;
printf("%c", "|:. "[ti]);
}
Sleep(100);
}
exit(0);
return 0;
}

View File

@ -0,0 +1,947 @@
; If set to 1, timing information is generated during music generation
; which is needed for Clinkster_GetInstrumentTrigger.
; Set it to 0 if you don't need this functionality.
%define CLINKSTER_GENERATE_TIMING_DATA 0
; Offset applied by Clinkster_GetPosition to compensate for graphics latency.
; Measured in samples (44100ths of a second).
; The default value of 2048 (corresponding to about 46 milliseconds) is
; appropriate for typical display latencies for high-framerate effects.
%define CLINKSTER_TIMER_OFFSET 2048
%include "music.asm"
;; ********** Definitions **********
global Clinkster_GenerateMusic
global _Clinkster_GenerateMusic@0
global Clinkster_StartMusic
global _Clinkster_StartMusic@0
global Clinkster_GetPosition
global _Clinkster_GetPosition@0
global Clinkster_GetInstrumentTrigger
global _Clinkster_GetInstrumentTrigger@8
global Clinkster_MusicBuffer
global _Clinkster_MusicBuffer
global Clinkster_TicksPerSecond
global _Clinkster_TicksPerSecond
global Clinkster_MusicLength
global _Clinkster_MusicLength
global Clinkster_NumTracks
global _Clinkster_NumTracks
%if CLINKSTER_GENERATE_TIMING_DATA
global Clinkster_NoteTiming
global _Clinkster_NoteTiming
%endif
global Clinkster_WavFileHeader
global _Clinkster_WavFileHeader
extern __imp__waveOutOpen@24
extern __imp__waveOutPrepareHeader@12
extern __imp__waveOutWrite@12
extern __imp__waveOutGetPosition@12
%define SAMPLE_RATE 44100
%define WAVE_SIZE 65536
;; ********** Public variables **********
section MusBuf bss align=4
Clinkster_MusicBuffer:
_Clinkster_MusicBuffer:
.align24
resw (TOTAL_SAMPLES*2)
resw 2 ; padding to catch extra write in conversion
section tps rdata align=4
Clinkster_TicksPerSecond:
_Clinkster_TicksPerSecond:
dd TICKS_PER_SECOND
section muslen rdata align=4
Clinkster_MusicLength:
_Clinkster_MusicLength:
dd MUSIC_LENGTH
section numtr rdata align=4
Clinkster_NumTracks:
_Clinkster_NumTracks:
dd NUMTRACKS
%if CLINKSTER_GENERATE_TIMING_DATA
section musdat bss align=4
Clinkster_NoteTiming:
_Clinkster_NoteTiming:
.align16
resd 2*(NUMTRACKS<<LOGNUMTICKS)
section timing data align=4
timing_ptr: dd Clinkster_NoteTiming
%endif
section WavFile rdata align=4
Clinkster_WavFileHeader:
_Clinkster_WavFileHeader:
db "RIFF"
dd 36+TOTAL_SAMPLES*4
db "WAVE"
db "fmt "
dd 16
dw 1,2
dd SAMPLE_RATE
dd SAMPLE_RATE*4
dw 4,16
db "data"
dd TOTAL_SAMPLES*4
;; ********** System structures **********
section WaveForm rdata align=1
_WaveFormat:
dw 1,2
dd SAMPLE_RATE
dd SAMPLE_RATE*4
dw 4,16,0
section WaveHdr data align=4
_WaveHdr:
dd Clinkster_MusicBuffer
dd (TOTAL_SAMPLES*4)
dd 0,0,0,0,0,0
section wavehand bss align=4
_WaveOutHandle:
.align16
resd 1
section WaveTime data align=4
_WaveTime:
dd 4,0,0,0,0,0,0,0
;; ********** Internal buffers **********
section MixBuf bss align=4
resd MAX_DELAY_LENGTH
_MixingBuffer:
.align24
resd TOTAL_SAMPLES
section InstrBuf bss align=4
_InstrumentBuffer:
.align16
resd MAX_INSTRUMENT_SUBSAMPLES
section InstrRen bss align=4
resd 256
_InstrumentRender:
.align16
resd MAX_INSTRUMENT_SUBSAMPLES
section InstrSto bss align=4
_InstrumentStore:
.align16
resd MAX_TOTAL_INSTRUMENT_SAMPLES
section InstrPoi bss align=4
_InstrumentPointers:
.align16
resd MAX_TRACK_INSTRUMENT_RENDERS+1
section wforms bss align=4
waveforms:
.align16
resd 6*WAVE_SIZE
;; ********** Instrument parameter access **********
section paramw rdata align=4
param_weights:
dd 0.125 ; Release
dd 0.125 ; Decay
dd 0.125 ; Attack
%if USES_GAIN
dd 0.125 ; Gain
%endif
%if USES_INDEXDECAY
dd 0.0009765625 ; IndexDecay
%endif
dd 0.0009765625 ; M PitchDecay
dd 0.0009765625 ; B PitchDecay
dd 0.083333333333 ; M Pitch
dd 0.083333333333 ; B Pitch
dd 0.0000152587890625 ; Volume
%if USES_PANNING
dd 0.0000152587890625 ; Volume
%endif
dd 0.03125 ; Sustain
dd 16307 ; RandomSeed
dd 1 ; Layers
dd 4096.0 ; Index
dd 0.125 ; Index Spread
dd 0.0009765625 ; M Detune
dd 0.0009765625 ; B Detune
dd 65536 ; ModWave
dd 65536 ; BaseWave
struc instr_params
ip_basewave: resd 1
ip_modwave: resd 1
ip_bdetune: resd 1
ip_mdetune: resd 1
ip_indexspr: resd 1
ip_index: resd 1
ip_layers: resd 1
ip_randomseed: resd 1
ip_sustain: resd 1
ip_volume: resd 1+USES_PANNING
ip_bpitch: resd 1
ip_mpitch: resd 1
ip_bpitchd: resd 1
ip_mpitchd: resd 1
%if USES_INDEXDECAY
ip_indexd: resd 1
%endif
%if USES_GAIN
ip_gain: resd 1
%endif
ip_attack: resd 1
ip_decay: resd 1
ip_release: resd 1
endstruc
%define ip_INT 0
%define ip_FLOAT 0
%define IP(f,t) dword [instrparams + ip_ %+ f + ip_ %+ t]
%define IPI(f,i,t) dword [instrparams + ip_ %+ f + ip_ %+ t + i]
;; ********** Internal constants and tables **********
section resamp rdata align=4
resamplefilter:
db -1,-2,-4,-4,-2,3,14,30,51,98,116,126
db 126,116,98,51,30,14,3,-2,-4,-4,-2,-1
resamplefilter_end:
FILTER_SIZE equ (resamplefilter_end-resamplefilter)
section wavestep rdata align=4
c_wavestep: dd 0.000030517578125
section basefreq rdata align=4
c_basefreq: dd 2.86698696365342
section halfnote rdata align=4
c_halfnote: dd 1.05946309435929
section finalamp rdata align=4
c_finalamp: dd 32767
section velfac rdata align=4
c_velocityfac: dd 0.007874015748031496
section delaystr rdata align=4
c_delaystr: dd DELAY_STRENGTH
section offset rdata align=4
c_timeoffset: dd CLINKSTER_TIMER_OFFSET*4
section tempo rdata align=4
c_ticklength: dd SUBSAMPLES_PER_TICK/4*4
section half rdata align=4
c_onehalf: dd 0.5
;; ********** Internal global variables **********
section vars bss align=8
vars_align16
layer_random: resd 1
stereo: resd 1 ; 0 for left channel, 2 for right channel
noteposptr: resd 1
notesamptr: resd 1
instrparams:
resb instr_params_size
layer_params:
layer_bfreq: resq 1
layer_mfreq: resq 1
layer_index: resq 1
layer_bpitch: resq 1
layer_mpitch: resq 1
layer_bpitchd: resq 1
layer_mpitchd: resq 1
%if USES_INDEXDECAY
layer_indexd: resq 1
%endif
%if USES_GAIN
layer_gain: resq 1
%endif
layer_attack: resq 1
layer_decay: resq 1
layer_release: resq 1
layer_phasetmp: resd 1
;; ********** Generate the sound for one layer **********
section mklayer text align=1
makelayer:
mov edx, layer_params
; Init random variables for layer
fild word [layer_random]
mov ecx, dword [layer_random]
ror ecx, cl
dec ecx
mov dword [layer_random], ecx
fld IP(bdetune, FLOAT)
fmul st0, st0
fmulp st1, st0
fadd st0, st1
fstp qword [edx]
add edx, byte 8
fild word [layer_random]
mov ecx, dword [layer_random]
ror ecx, cl
dec ecx
mov dword [layer_random], ecx
fld IP(mdetune, FLOAT)
fmul st0, st0
fmulp st1, st0
fadd st0, st1
fstp qword [edx]
add edx, byte 8
fild word [layer_random]
mov ecx, dword [layer_random]
ror ecx, cl
dec ecx
mov dword [layer_random], ecx
fmul IP(indexspr, FLOAT)
fadd IP(index, FLOAT)
fstp qword [edx]
add edx, byte 8
; Init exponentiated variables for layer
mov edi, instrparams+ip_bpitch
mov ecx, 7+USES_INDEXDECAY+USES_GAIN
.powloop:
fld dword [edi]
fld1
fld st1
fprem
fstp st1
f2xm1
fld1
faddp st1, st0
fscale
fstp qword [edx]
add edx, byte 8
fstp st0
scasd
loop .powloop
mov ebx, edx ; phasetmp
; Loop over samples
fldz ; b phase
fldz ; m phase
mov edi, _InstrumentBuffer
; Calculate max note size
xor eax, eax
%if USES_VELOCITY
lodsb ; Skip velocity
%endif
%if USES_LONG_NOTES
cmp [esi], byte 0
jge near .short_notelen
lodsb
not al
shl eax, 8
.short_notelen:
%endif
lodsb ; Length of longest note with this tone
mov edx, SUBSAMPLES_PER_TICK
mul edx
add eax, MAX_RELEASE_SUBSAMPLES
xchg ecx, eax
.sampleloop:
mov edx, layer_params
; Look up mod wave
fist dword [ebx]
mov eax, IP(modwave,INT)
mov ax, [ebx]
fld dword [waveforms + eax*4]
; Adjust by index
fmul qword [edx + 2*8] ; layer_index
fadd st0, st2
; Look up base wave
fistp dword [ebx]
mov eax, IP(basewave,INT)
mov ax, [ebx]
fld dword [waveforms + eax*4]
; Update phases
fld qword [edx] ; layer_bfreq
add edx, byte 8
fmul qword [edx + 2*8] ; layer_bpitch
faddp st3, st0
fld qword [edx] ; layer_mfreq
add edx, byte 8
fmul qword [edx + 2*8] ; layer_mpitch
faddp st2, st0
%if USES_INDEXDECAY
fld qword [edx] ; layer_index
fld1
fadd st1, st0
fsubp st1, st0
%endif
add edx, byte 8
; Update pitches: p := (p-1)*d+1
fld1
fld qword [edx] ; layer_bpitch
fsub st0, st1
fmul qword [edx + 2*8] ; layer_bpitchd
faddp st1, st0
fstp qword [edx] ; layer_bpitch
add edx, byte 8
fld1
fld qword [edx] ; layer_mpitch
fsub st0, st1
fmul qword [edx + 2*8] ; layer_mpitchd
faddp st1, st0
fstp qword [edx] ; layer_mpitch
add edx, byte 8
%if USES_INDEXDECAY
fmul qword [edx + 2*8] ; layer_indexd
fstp qword [edx - 3*8] ; layer_index
%endif
; Add to existing layers
fadd dword [edi]
fstp dword [edi]
scasd
loop .sampleloop
fstp st0
fstp st0
ret
;; ********** Interpolate one section of amplitude envelope **********
section adsr text align=1
apply_adsr:
; On condition g:
; st0 = amplitude target
; st1 = amplitude
; st2 = velocity / nlayers
; eax = number of samples
; ecx = sample index
; On condition le:
; st0 = number of samples
; st1 = amplitude target
; st2 = amplitude
; st3 = velocity / nlayers
; ecx = sample index
push eax
jg .integer_length
fimul dword [c_finalamp]
fistp dword [esp]
.integer_length:
fsub st0, st1
fild dword [esp]
pop eax
add eax, ecx
fdivp st1, st0
.adsrloop:
fld dword [_InstrumentBuffer + ecx*4]
fmul st0, st3 ; velocity / nlayers
fmul st0, st2 ; envelope value
%if USES_GAIN
fld1
fsubr qword [layer_gain]
fmul st0, st1
fmul st0, st1
fld1
faddp st1, st0
fdivr qword [layer_gain]
fsqrt
fmulp st1, st0
%endif
fstp dword [_InstrumentRender + ecx*4]
fadd st1, st0
inc ecx
cmp ecx, eax
jl .adsrloop
fstp st0
ret
;; ********** Main music generation **********
section genMus text align=1
Clinkster_GenerateMusic:
_Clinkster_GenerateMusic@0:
pusha
fninit
; Make waveforms
mov edi, waveforms
%if USES_SINE
fldz
mov ecx, WAVE_SIZE
.sineloop:
fadd dword [c_wavestep]
fld st0
fldpi
fmulp st1, st0
fsin
fstp dword [edi]
scasd
loop .sineloop
fstp st0
%endif
%if USES_SAWTOOTH
fld1
fchs
mov ecx, WAVE_SIZE
.sawtoothloop:
fadd dword [c_wavestep]
fst dword [edi]
scasd
loop .sawtoothloop
fstp st0
%endif
%if USES_SQUARE
fld1
fchs
mov ecx, WAVE_SIZE
.squareloop:
cmp ecx, WAVE_SIZE/2
jne .notflipsq
fabs
.notflipsq:
fst dword [edi]
scasd
loop .squareloop
fstp st0
%endif
%if USES_PARABOLA
fld1
fchs
mov ecx, WAVE_SIZE
.parabolaloop:
fadd dword [c_wavestep]
fld st0
fmul st0, st1
fadd st0, st0
fld1
fsubp st1, st0
fstp dword [edi]
scasd
loop .parabolaloop
fstp st0
%endif
%if USES_TRIANGLE
fld1
fchs
mov ecx, WAVE_SIZE
.triangleloop:
fadd dword [c_wavestep]
fld st0
fadd st0, st1
fabs
fld1
fsubp st1, st0
fstp dword [edi]
scasd
loop .triangleloop
fstp st0
%endif
%if USES_NOISE
fld1
fchs
mov ecx, WAVE_SIZE
.noiseloop:
fadd dword [c_wavestep]
fldpi
fmulp st1, st0
fsin
fst dword [edi]
scasd
loop .noiseloop
fstp st0
%endif
.stereoloop:
mov dword [noteposptr], _NotePositions
mov dword [notesamptr], _NoteSamples
xor eax, eax
mov edi, _MixingBuffer
mov ecx, TOTAL_SAMPLES
rep stosd
mov esi, _InstrumentData
%if USES_DELAY
jmp short .trackloop
.delay:
mov eax, dword [stereo]
mov edx, (LEFT_DELAY_LENGTH-RIGHT_DELAY_LENGTH)*4/2
mul edx
sub eax, LEFT_DELAY_LENGTH*4
mov edi, _MixingBuffer
mov ecx, TOTAL_SAMPLES
.delayloop:
fld dword [edi+eax]
fmul dword [c_delaystr]
fadd dword [edi]
fstp dword [edi]
scasd
loop .delayloop
%endif
.trackloop:
; ESI = instr data
mov edi, instrparams
mov ecx, instr_params_size/4
.ploop:
lodsb
movsx eax, al
push eax
fild dword [esp]
pop eax
fmul dword [param_weights-4+ecx*4]
fstp dword [edi]
scasd
loop .ploop
mov edi, instrparams+ip_bpitchd
mov ecx, 2+USES_INDEXDECAY
.cubeloop:
fld dword [edi]
fld st0
fmul st0, st0
fmulp st1, st0
fstp dword [edi]
scasd
loop .cubeloop
mov ebx, _InstrumentPointers
mov dword [ebx], _InstrumentStore ; store first instrument instance address
fld dword [c_basefreq]
; Loop over instrument tones
.toneloop:
xor eax, eax
lodsb ; Tone
.freqloop:
fmul dword [c_halfnote]
dec eax
jge .freqloop
; random seed for channel = RandomSeed * 16307 + channel * 12042
mov eax, dword [stereo]
mov edx, 12042/2
mul edx
add eax, IP(randomseed,INT)
xchg ecx, eax
mov dword [layer_random], ecx
xor eax, eax
mov edi, _InstrumentBuffer
mov ecx, MAX_INSTRUMENT_SUBSAMPLES
rep stosd
; Loop over layers
mov ecx, IP(layers,INT)
.layerloop:
pusha
call makelayer
popa
loop .layerloop
.lengthloop:
%if USES_VELOCITY
lodsb
movsx eax, al
push eax
fild dword [esp]
pop eax
fmul dword [c_velocityfac]
%else
fld1
%endif
fidiv IP(layers,INT)
xor ecx, ecx ; sample index
fldz ; amplitude level
fld1 ; attack amplitude target
fld qword [layer_attack]; attack length
call apply_adsr
fld IP(sustain,FLOAT) ; decay amplitude target
fld qword [layer_decay] ; decay length
call apply_adsr
xor eax, eax
%if USES_LONG_NOTES
cmp [esi], byte 0
jge near .short_notelen
lodsb
not al
shl eax, 8
.short_notelen:
%endif
lodsb ; note length in ticks
mov edx, SUBSAMPLES_PER_TICK
mul edx
sub eax, ecx ; note length exclusing attack and decay
jle .nosustain ; attack + decay overflows note length
fld IP(sustain,FLOAT) ; sustain amplitude target
call apply_adsr
.nosustain:
fldz ; release amplitude target
fld qword [layer_release];release length
call apply_adsr
fldz ; padding amplitude
fld1 ; padding length
call apply_adsr
fstp st0
fstp st0
; Resampling
mov edi, [ebx] ; instrument instance address
add ebx, byte 4
xchg edx, eax
mov ebp, _InstrumentRender - FILTER_SIZE*4
.resampleloop:
fldz
mov ecx, FILTER_SIZE
.filterloop:
movsx eax, byte [resamplefilter + ecx - 1]
push eax
fild dword [esp]
pop eax
fmul dword [ebp + ecx*4]
faddp st1, st0
loop .filterloop
%if USES_PANNING
mov eax, dword [stereo]
fmul IPI(volume,eax*2,FLOAT)
%else
fmul IP(volume,FLOAT)
%endif
fstp dword [edi]
scasd
add ebp, byte 4*4
sub edx, byte 4
jg .resampleloop
mov [ebx], edi ; store instrument instance address
cmp [esi], byte 0
jne near .lengthloop
lodsb
cmp [esi], byte 0
jge near .toneloop
lodsb
fstp st0
; Mixing
mov ebx, _InstrumentPointers
mov ebp, _MixingBuffer
xchg esi, dword [notesamptr]
.noteloop:
xchg esi, dword [noteposptr]
xor eax, eax
cmp [esi], byte 0
jge near .short_notepos
lodsb
not al
shl eax, 8
.short_notepos:
lodsb
mov edx, SUBSAMPLES_PER_TICK/4*4
mul edx
add ebp, eax
%if CLINKSTER_GENERATE_TIMING_DATA
mov ecx, SUBSAMPLES_PER_TICK/4*4
div ecx
xchg edx, eax
mov edi, [timing_ptr]
mov eax, [edi]
mov ecx, edx
rep stosd
mov [timing_ptr], edi
add eax, edx
stosd
%endif
xchg esi, dword [noteposptr]
xor eax, eax
lodsb
mov edx, dword [ebx + eax*4] ; Instrument instance ptr
mov edi, ebp
.mixloop:
fld dword [edx]
fadd dword [edi]
fstp dword [edi]
scasd
add edx, byte 4
cmp edx, dword [ebx + eax*4 + 4]
jl .mixloop
cmp [esi], byte 0
jge near .noteloop
lodsb
xchg esi, dword [notesamptr]
%if CLINKSTER_GENERATE_TIMING_DATA
mov ecx, 1<<LOGNUMTICKS
mov edi, [timing_ptr]
mov eax, [edi]
sub ecx, eax
rep stosd
mov [timing_ptr], edi
%endif
cmp [esi], byte 0
jge near .trackloop
lodsb
%if USES_DELAY
cmp [esi], byte 0
jge near .delay
%endif
; Clamp and convert to shorts
fld1
mov edi, Clinkster_MusicBuffer
mov ecx, TOTAL_SAMPLES
add edi, dword [stereo]
.sloop:
fld dword [_MixingBuffer + ecx*4]
fcomi st0, st1
fcmovnb st0, st1
fchs
fcomi st0, st1
fcmovnb st0, st1
fchs
fimul dword [c_finalamp]
fistp word [edi + ecx*4]
loop .sloop
fstp st0
xor byte [stereo], 2
jne .stereoloop
popa
ret
;; ********** Start music **********
section startmus text align=1
Clinkster_StartMusic:
_Clinkster_StartMusic@0:
; Start music
push byte 0
push byte 0
push byte 0
push _WaveFormat
push byte -1
push _WaveOutHandle
call [__imp__waveOutOpen@24]
push byte 32 ; sizeof(WAVEHDR)
push _WaveHdr
push dword [_WaveOutHandle] ; waveOutHandle
call [__imp__waveOutPrepareHeader@12]
push byte 32 ; sizeof(WAVEHDR)
push _WaveHdr
push dword [_WaveOutHandle]
call [__imp__waveOutWrite@12]
ret
;; ********** Get current play position **********
section getpos text align=1
Clinkster_GetPosition:
_Clinkster_GetPosition@0:
push byte 32 ; sizeof(MMTIME)
push _WaveTime
push dword [_WaveOutHandle]
call [__imp__waveOutGetPosition@12]
fild dword [_WaveTime+4]
%if CLINKSTER_TIMER_OFFSET>0
fiadd dword [c_timeoffset]
%endif
fidiv dword [c_ticklength]
ret
;; ********** Get time since instrument trigger **********
%if CLINKSTER_GENERATE_TIMING_DATA
section insttrig text align=1
Clinkster_GetInstrumentTrigger:
_Clinkster_GetInstrumentTrigger@8:
cvttss2si eax, [esp+8]
mov ecx, [esp+4]
shl ecx, LOGNUMTICKS+2
fld dword [esp+8]
fisub dword [Clinkster_NoteTiming+ecx+eax*4]
ret 8
%endif

View File

@ -0,0 +1,57 @@
#ifndef _CLINKSTER_H_
#define _CLINKSTER_H_
struct sample {
short left,right;
};
extern "C" {
// Generate the whole music in to music buffer. When this function
// returns, Clinkster_MusicBuffer will be filled with sound data,
// and Clinkster_StartMusic can be called.
void __stdcall Clinkster_GenerateMusic();
// Play the music
void __stdcall Clinkster_StartMusic();
// Returns how much of the music has currently been played.
// Use this function as the timer for the visuals in your intro.
// Returned value is measured in music ticks (pattern rows).
float __stdcall Clinkster_GetPosition();
// Used for timing intro events to music notes.
// Returns how long ago (in ticks) a note was last triggered in the
// specified track, starting from the specified position (in ticks).
// If the track contains no note before the specified position,
// the position is returned back (as if there was a note at time 0).
// Note that the track number refers to post-conversion track numbers.
// Consult the text output from the conversion script to see at which
// track each original track/instrument combination has ended up.
float __stdcall Clinkster_GetInstrumentTrigger(int track, float position);
// Buffer containing the music.
extern struct sample Clinkster_MusicBuffer[];
// The tick rate of the music.
extern const float Clinkster_TicksPerSecond;
// The length of the music in ticks.
extern const unsigned int Clinkster_MusicLength;
// The number of tracks in the music.
extern const unsigned int Clinkster_NumTracks;
// Timing data used by Clinkster_GetInstrumentTrigger.
// Consists of NUMTRACKS blocks of (1 << LOGNUMTICKS) ints
// (constants defined in music.asm) where index i for a track
// holds the last tick (at or prior to i) where a note was
// triggered in that track.
extern unsigned int Clinkster_TimingData[];
// Wav file header to use if you want to write the music to disk.
// Write these 44 bytes followed by Clinkster_MusicBuffer with a
// length of Clinkster_WavFileHeader[10].
extern unsigned int Clinkster_WavFileHeader[11];
};
#endif

View File

@ -0,0 +1,11 @@
extern Clinkster_GenerateMusic
extern Clinkster_StartMusic
extern Clinkster_GetPosition
extern Clinkster_GetInstrumentTrigger
extern Clinkster_MusicBuffer
extern Clinkster_NoteTiming
extern Clinkster_TicksPerSecond
extern Clinkster_MusicLength
extern Clinkster_NumTracks

View File

@ -0,0 +1,962 @@
; If set to 1, timing information is generated during music generation
; which is needed for Clinkster_GetInstrumentTrigger.
; Set it to 0 if you don't need this functionality.
%define CLINKSTER_GENERATE_TIMING_DATA 0
; Offset applied by Clinkster_GetPosition to compensate for graphics latency.
; Measured in samples (44100ths of a second).
; The default value of 2048 (corresponding to about 46 milliseconds) is
; appropriate for typical display latencies for high-framerate effects.
%define CLINKSTER_TIMER_OFFSET 2048
%include "music.asm"
;; ********** Definitions **********
global Clinkster_GenerateMusic
global _Clinkster_GenerateMusic@0
global Clinkster_StartMusic
global _Clinkster_StartMusic@0
global Clinkster_GetPosition
global _Clinkster_GetPosition@0
global Clinkster_GetInstrumentTrigger
global _Clinkster_GetInstrumentTrigger@8
global Clinkster_MusicBuffer
global _Clinkster_MusicBuffer
global Clinkster_TicksPerSecond
global _Clinkster_TicksPerSecond
global Clinkster_MusicLength
global _Clinkster_MusicLength
global Clinkster_NumTracks
global _Clinkster_NumTracks
%if CLINKSTER_GENERATE_TIMING_DATA
global Clinkster_NoteTiming
global _Clinkster_NoteTiming
%endif
global Clinkster_WavFileHeader
global _Clinkster_WavFileHeader
extern __imp__waveOutOpen@24
extern __imp__waveOutPrepareHeader@12
extern __imp__waveOutWrite@12
extern __imp__waveOutGetPosition@12
extern __imp__CreateThread@24
extern __imp__WaitForSingleObject@8
%define SAMPLE_RATE 44100
%define WAVE_SIZE 65536
;; ********** Public variables **********
section MusBuf bss align=4
Clinkster_MusicBuffer:
_Clinkster_MusicBuffer:
.align24
resw (TOTAL_SAMPLES*2)
resw 2 ; padding to catch extra write in conversion
section tps rdata align=4
Clinkster_TicksPerSecond:
_Clinkster_TicksPerSecond:
dd TICKS_PER_SECOND
section muslen rdata align=4
Clinkster_MusicLength:
_Clinkster_MusicLength:
dd MUSIC_LENGTH
section numtr rdata align=4
Clinkster_NumTracks:
_Clinkster_NumTracks:
dd NUMTRACKS
%if CLINKSTER_GENERATE_TIMING_DATA
section musdat bss align=4
Clinkster_NoteTiming:
_Clinkster_NoteTiming:
.align16
resd 2*(NUMTRACKS<<LOGNUMTICKS)
section timing data align=4
timing_ptr: dd Clinkster_NoteTiming
%endif
section WavFile rdata align=4
Clinkster_WavFileHeader:
_Clinkster_WavFileHeader:
db "RIFF"
dd 36+TOTAL_SAMPLES*4
db "WAVE"
db "fmt "
dd 16
dw 1,2
dd SAMPLE_RATE
dd SAMPLE_RATE*4
dw 4,16
db "data"
dd TOTAL_SAMPLES*4
;; ********** System structures **********
section WaveForm rdata align=1
_WaveFormat:
dw 1,2
dd SAMPLE_RATE
dd SAMPLE_RATE*4
dw 4,16,0
section WaveHdr data align=4
_WaveHdr:
dd Clinkster_MusicBuffer
dd (TOTAL_SAMPLES*4)
dd 0,0,0,0,0,0
section wavehand bss align=4
_WaveOutHandle:
.align16
resd 1
section WaveTime data align=4
_WaveTime:
dd 4,0,0,0,0,0,0,0
;; ********** Internal buffers **********
section wforms bss align=4
waveforms:
.align16
resd 6*WAVE_SIZE
;; ********** Instrument parameter access **********
section paramw rdata align=4
param_weights:
dd 0.125 ; Release
dd 0.125 ; Decay
dd 0.125 ; Attack
%if USES_GAIN
dd 0.125 ; Gain
%endif
%if USES_INDEXDECAY
dd 0.0009765625 ; IndexDecay
%endif
dd 0.0009765625 ; M PitchDecay
dd 0.0009765625 ; B PitchDecay
dd 0.083333333333 ; M Pitch
dd 0.083333333333 ; B Pitch
dd 0.0000152587890625 ; Volume
%if USES_PANNING
dd 0.0000152587890625 ; Volume
%endif
dd 0.03125 ; Sustain
dd 16307 ; RandomSeed
dd 1 ; Layers
dd 4096.0 ; Index
dd 0.125 ; Index Spread
dd 0.0009765625 ; M Detune
dd 0.0009765625 ; B Detune
dd 65536 ; ModWave
dd 65536 ; BaseWave
struc instr_params
ip_basewave: resd 1
ip_modwave: resd 1
ip_bdetune: resd 1
ip_mdetune: resd 1
ip_indexspr: resd 1
ip_index: resd 1
ip_layers: resd 1
ip_randomseed: resd 1
ip_sustain: resd 1
ip_volume: resd 1+USES_PANNING
ip_bpitch: resd 1
ip_mpitch: resd 1
ip_bpitchd: resd 1
ip_mpitchd: resd 1
%if USES_INDEXDECAY
ip_indexd: resd 1
%endif
%if USES_GAIN
ip_gain: resd 1
%endif
ip_attack: resd 1
ip_decay: resd 1
ip_release: resd 1
endstruc
%define ip_INT 0
%define ip_FLOAT 0
%define IP(f,t) dword [dword ebx + g_instrparams + ip_ %+ f + ip_ %+ t]
%define IPI(f,i,t) dword [dword ebx + g_instrparams + ip_ %+ f + ip_ %+ t + i]
;; ********** Internal constants and tables **********
section resamp rdata align=4
resamplefilter:
db -1,-2,-4,-4,-2,3,14,30,51,98,116,126
db 126,116,98,51,30,14,3,-2,-4,-4,-2,-1
resamplefilter_end:
FILTER_SIZE equ (resamplefilter_end-resamplefilter)
section wavestep rdata align=4
c_wavestep: dd 0.000030517578125
section basefreq rdata align=4
c_basefreq: dd 2.86698696365342
section halfnote rdata align=4
c_halfnote: dd 1.05946309435929
section finalamp rdata align=4
c_finalamp: dd 32767
section velfac rdata align=4
c_velocityfac: dd 0.007874015748031496
section delaystr rdata align=4
c_delaystr: dd DELAY_STRENGTH
section offset rdata align=4
c_timeoffset: dd CLINKSTER_TIMER_OFFSET*4
section tempo rdata align=4
c_ticklength: dd SUBSAMPLES_PER_TICK/4*4
section half rdata align=4
c_onehalf: dd 0.5
;; ********** Internal global variables **********
struc globalvars
g_phasetemp: resd 1
g_layer_random: resd 1
g_stereo: resd 1 ; 0 for left channel, 2 for right channel
g_noteposptr: resd 1
g_notesamptr: resd 1
g_instrparams: resb instr_params_size
g_layerparams: resq 0
g_layer_bfreq: resq 1
g_layer_mfreq: resq 1
g_layer_index: resq 1
g_layer_bpitch: resq 1
g_layer_mpitch: resq 1
g_layer_bpitchd: resq 1
g_layer_mpitchd: resq 1
%if USES_INDEXDECAY
g_layer_indexd: resq 1
%endif
%if USES_GAIN
g_layer_gain: resq 1
%endif
g_layer_attack: resq 1
g_layer_decay: resq 1
g_layer_release: resq 1
alignb 256
g_InstrumentPointers:
resd MAX_TRACK_INSTRUMENT_RENDERS+1
resd MAX_DELAY_LENGTH
alignb 16777216
g_MixingBuffer:
resd TOTAL_SAMPLES
alignb 16777216
g_InstrumentBuffer:
resd MAX_INSTRUMENT_SUBSAMPLES
resd 256
alignb 16777216
g_InstrumentRender:
resd MAX_INSTRUMENT_SUBSAMPLES
alignb 16777216
g_InstrumentStore:
resd MAX_TOTAL_INSTRUMENT_SAMPLES
endstruc
section vars bss align=8
vars_align16
globals:
resb globalvars_size
resb globalvars_size
;; ********** Generate the sound for one layer **********
section mklayer text align=1
makelayer:
lea edx, [dword ebx + g_layerparams]
; Init random variables for layer
fild word [dword ebx + g_layer_random]
mov ecx, dword [dword ebx + g_layer_random]
ror ecx, cl
dec ecx
mov dword [dword ebx + g_layer_random], ecx
fld IP(bdetune, FLOAT)
fmul st0, st0
fmulp st1, st0
fadd st0, st1
fstp qword [edx]
add edx, byte 8
fild word [dword ebx + g_layer_random]
mov ecx, dword [dword ebx + g_layer_random]
ror ecx, cl
dec ecx
mov dword [dword ebx + g_layer_random], ecx
fld IP(mdetune, FLOAT)
fmul st0, st0
fmulp st1, st0
fadd st0, st1
fstp qword [edx]
add edx, byte 8
fild word [dword ebx + g_layer_random]
mov ecx, dword [dword ebx + g_layer_random]
ror ecx, cl
dec ecx
mov dword [dword ebx + g_layer_random], ecx
fmul IP(indexspr, FLOAT)
fadd IP(index, FLOAT)
fstp qword [edx]
add edx, byte 8
; Init exponentiated variables for layer
lea edi, [dword ebx + g_instrparams+ip_bpitch]
mov ecx, 7+USES_INDEXDECAY+USES_GAIN
.powloop:
fld dword [edi]
fld1
fld st1
fprem
fstp st1
f2xm1
fld1
faddp st1, st0
fscale
fstp qword [edx]
add edx, byte 8
fstp st0
scasd
loop .powloop
; Loop over samples
fldz ; b phase
fldz ; m phase
lea edi, [dword ebx + g_InstrumentBuffer]
; Calculate max note size
xor eax, eax
%if USES_VELOCITY
lodsb ; Skip velocity
%endif
%if USES_LONG_NOTES
cmp [esi], byte 0
jge near .short_notelen
lodsb
not al
shl eax, 8
.short_notelen:
%endif
lodsb ; Length of longest note with this tone
mov edx, SUBSAMPLES_PER_TICK
mul edx
add eax, MAX_RELEASE_SUBSAMPLES
xchg ecx, eax
.sampleloop:
lea edx, [dword ebx + g_layerparams]
; Look up and normalize mod wave
fist dword [ebx]
mov eax, IP(modwave,INT)
mov ax, [ebx]
fld dword [waveforms + eax*4]
; Adjust by index
fmul qword [edx + 2*8] ; layer_index
fadd st0, st2
; Look up base wave
fistp dword [ebx]
mov eax, IP(basewave,INT)
mov ax, [ebx]
fld dword [waveforms + eax*4]
; Update phases
fld qword [edx] ; layer_bfreq
add edx, byte 8
fmul qword [edx + 2*8] ; layer_bpitch
faddp st3, st0
fld qword [edx] ; layer_mfreq
add edx, byte 8
fmul qword [edx + 2*8] ; layer_mpitch
faddp st2, st0
%if USES_INDEXDECAY
fld qword [edx] ; layer_index
fld1
fadd st1, st0
fsubp st1, st0
%endif
add edx, byte 8
; Update pitches: p := (p-1)*d+1
.update:
fld1
fld qword [edx] ; layer_(b/m)pitch
fsub st0, st1
fmul qword [edx + 2*8] ; layer_(b/m)pitchd
faddp st1, st0
fstp qword [edx] ; layer_(b/m)pitch
add edx, byte 8
neg ecx
js .update
%if USES_INDEXDECAY
fmul qword [edx + 2*8] ; layer_indexd
fstp qword [edx - 3*8] ; layer_index
%endif
; Add to existing layers
fadd dword [edi]
fstp dword [edi]
scasd
loop .sampleloop
fstp st0
fstp st0
ret
;; ********** Interpolate one section of amplitude envelope **********
section adsr text align=1
apply_adsr:
; On condition g:
; st0 = amplitude target
; st1 = amplitude
; st2 = velocity / nlayers
; eax = number of samples
; ecx = sample index
; On condition le:
; st0 = number of samples
; st1 = amplitude target
; st2 = amplitude
; st3 = velocity / nlayers
; ecx = sample index
push eax
jg .integer_length
fimul dword [c_finalamp]
fistp dword [esp]
.integer_length:
fsub st0, st1
fild dword [esp]
pop eax
add eax, ecx
fdivp st1, st0
.adsrloop:
fld dword [dword ebx + g_InstrumentBuffer + ecx*4]
fmul st0, st3 ; velocity / nlayers
fmul st0, st2 ; envelope value
%if USES_GAIN
fld1
fsubr qword [dword ebx + g_layer_gain]
fmul st0, st1
fmul st0, st1
fld1
faddp st1, st0
fdivr qword [dword ebx + g_layer_gain]
fsqrt
fmulp st1, st0
%endif
fstp dword [dword ebx + g_InstrumentRender + ecx*4]
fadd st1, st0
inc ecx
cmp ecx, eax
jl .adsrloop
fstp st0
ret
;; ********** Main music generation **********
section genMus text align=1
Clinkster_GenerateMusic:
_Clinkster_GenerateMusic@0:
pusha
fninit
; Make waveforms
mov edi, waveforms
%if USES_SINE
fldz
mov ecx, WAVE_SIZE
.sineloop:
fadd dword [c_wavestep]
fld st0
fldpi
fmulp st1, st0
fsin
fstp dword [edi]
scasd
loop .sineloop
fstp st0
%endif
%if USES_SAWTOOTH
fld1
fchs
mov ecx, WAVE_SIZE
.sawtoothloop:
fadd dword [c_wavestep]
fst dword [edi]
scasd
loop .sawtoothloop
fstp st0
%endif
%if USES_SQUARE
fld1
fchs
mov ecx, WAVE_SIZE
.squareloop:
cmp ecx, WAVE_SIZE/2
jne .notflipsq
fabs
.notflipsq:
fst dword [edi]
scasd
loop .squareloop
fstp st0
%endif
%if USES_PARABOLA
fld1
fchs
mov ecx, WAVE_SIZE
.parabolaloop:
fadd dword [c_wavestep]
fld st0
fmul st0, st1
fadd st0, st0
fld1
fsubp st1, st0
fstp dword [edi]
scasd
loop .parabolaloop
fstp st0
%endif
%if USES_TRIANGLE
fld1
fchs
mov ecx, WAVE_SIZE
.triangleloop:
fadd dword [c_wavestep]
fld st0
fadd st0, st1
fabs
fld1
fsubp st1, st0
fstp dword [edi]
scasd
loop .triangleloop
fstp st0
%endif
%if USES_NOISE
fld1
fchs
mov ecx, WAVE_SIZE
.noiseloop:
fadd dword [c_wavestep]
fldpi
fmulp st1, st0
fsin
fst dword [edi]
scasd
loop .noiseloop
fstp st0
%endif
push byte 0 ; lpThreadId
push byte 0 ; dwCreationFlags
push byte 0 ; lpParameter
push makechannel
push byte 0 ; dwStackSize
push byte 0 ; lpThreadAttributes
call [__imp__CreateThread@24]
push byte -1
push eax
push byte 2
call makechannel
call [__imp__WaitForSingleObject@8]
popa
ret
makechannel:
; eax = channel (0 or 2)
mov eax, [esp + 4]
mov edx, globalvars_size/2
mul edx
mov ebx, globals
add ebx, eax
mov eax, [esp + 4]
mov [dword ebx + g_stereo], eax
mov dword [dword ebx + g_noteposptr], _NotePositions
mov dword [dword ebx + g_notesamptr], _NoteSamples
mov esi, _InstrumentData
%if USES_DELAY
jmp short .trackloop
.delay:
mov eax, dword [dword ebx + g_stereo]
mov edx, (LEFT_DELAY_LENGTH-RIGHT_DELAY_LENGTH)*4/2
mul edx
sub eax, LEFT_DELAY_LENGTH*4
lea edi, [dword ebx + g_MixingBuffer]
mov ecx, TOTAL_SAMPLES
.delayloop:
fld dword [edi+eax]
fmul dword [c_delaystr]
fadd dword [edi]
fstp dword [edi]
scasd
loop .delayloop
%endif
.trackloop:
; ESI = instr data
lea edi, [dword ebx + g_instrparams]
mov ecx, instr_params_size/4
.ploop:
lodsb
movsx eax, al
push eax
fild dword [esp]
pop eax
fmul dword [param_weights-4+ecx*4]
fstp dword [edi]
scasd
loop .ploop
lea edi, [dword ebx + g_instrparams+ip_bpitchd]
mov ecx, 2+USES_INDEXDECAY
.cubeloop:
fld dword [edi]
fld st0
fmul st0, st0
fmulp st1, st0
fstp dword [edi]
scasd
loop .cubeloop
lea ebp, [dword ebx + g_InstrumentPointers]
lea edi, [dword ebx + g_InstrumentStore]
mov dword [ebp], edi ; store first instrument instance address
fld dword [c_basefreq]
; Loop over instrument tones
.toneloop:
xor eax, eax
lodsb ; Tone
.freqloop:
fmul dword [c_halfnote]
dec eax
jge .freqloop
; random seed for channel = RandomSeed * 16307 + channel * 12042
mov eax, dword [dword ebx + g_stereo]
mov edx, 12042/2
mul edx
add eax, IP(randomseed,INT)
xchg ecx, eax
mov dword [dword ebx + g_layer_random], ecx
xor eax, eax
lea edi, [dword ebx + g_InstrumentBuffer]
mov ecx, MAX_INSTRUMENT_SUBSAMPLES
rep stosd
; Loop over layers
mov ecx, IP(layers,INT)
.layerloop:
pusha
call makelayer
popa
loop .layerloop
.lengthloop:
%if USES_VELOCITY
lodsb
movsx eax, al
push eax
fild dword [esp]
pop eax
fmul dword [c_velocityfac]
%else
fld1
%endif
fidiv IP(layers,INT)
xor ecx, ecx ; sample index
fldz ; amplitude level
fld1 ; attack amplitude target
fld qword [dword ebx + g_layer_attack]; attack length
call apply_adsr
fld IP(sustain,FLOAT) ; decay amplitude target
fld qword [dword ebx + g_layer_decay] ; decay length
call apply_adsr
xor eax, eax
%if USES_LONG_NOTES
cmp [esi], byte 0
jge near .short_notelen
lodsb
not al
shl eax, 8
.short_notelen:
%endif
lodsb ; note length in ticks
mov edx, SUBSAMPLES_PER_TICK
mul edx
sub eax, ecx ; note length exclusing attack and decay
jle .nosustain ; attack + decay overflows note length
fld IP(sustain,FLOAT) ; sustain amplitude target
call apply_adsr
.nosustain:
fldz ; release amplitude target
fld qword [dword ebx + g_layer_release];release length
call apply_adsr
fldz ; padding amplitude
fld1 ; padding length
call apply_adsr
fstp st0
fstp st0
; Resampling
push esi
mov edi, [ebp] ; instrument instance address
add ebp, byte 4
xchg edx, eax
lea esi, [dword ebx + g_InstrumentRender - FILTER_SIZE*4]
.resampleloop:
fldz
mov ecx, FILTER_SIZE
.filterloop:
movsx eax, byte [resamplefilter + ecx - 1]
push eax
fild dword [esp]
pop eax
fmul dword [esi + ecx*4]
faddp st1, st0
loop .filterloop
%if USES_PANNING
mov eax, dword [dword ebx + g_stereo]
fmul IPI(volume,eax*2,FLOAT)
%else
fmul IP(volume,FLOAT)
%endif
fstp dword [edi]
scasd
add esi, byte 4*4
sub edx, byte 4
jg .resampleloop
mov [ebp], edi ; store instrument instance address
pop esi
cmp [esi], byte 0
jne near .lengthloop
lodsb
cmp [esi], byte 0
jge near .toneloop
lodsb
fstp st0
; Mixing
lea ebp, [dword ebx + g_MixingBuffer]
xchg esi, dword [dword ebx + g_notesamptr]
.noteloop:
xchg esi, dword [dword ebx + g_noteposptr]
xor eax, eax
cmp [esi], byte 0
jge near .short_notepos
lodsb
not al
shl eax, 8
.short_notepos:
lodsb
mov edx, SUBSAMPLES_PER_TICK/4*4
mul edx
add ebp, eax
%if CLINKSTER_GENERATE_TIMING_DATA
mov ecx, SUBSAMPLES_PER_TICK/4*4
div ecx
xchg edx, eax
mov edi, [timing_ptr]
mov eax, [edi]
mov ecx, edx
rep stosd
mov [timing_ptr], edi
add eax, edx
stosd
%endif
xchg esi, dword [dword ebx + g_noteposptr]
xor eax, eax
lodsb
mov edx, dword [dword ebx + g_InstrumentPointers + eax*4] ; Instrument instance ptr
mov edi, ebp
.mixloop:
fld dword [edx]
fadd dword [edi]
fstp dword [edi]
scasd
add edx, byte 4
cmp edx, dword [dword ebx + g_InstrumentPointers + eax*4 + 4]
jl .mixloop
cmp [esi], byte 0
jge near .noteloop
lodsb
xchg esi, dword [dword ebx + g_notesamptr]
%if CLINKSTER_GENERATE_TIMING_DATA
mov ecx, 1<<LOGNUMTICKS
mov edi, [timing_ptr]
mov eax, [edi]
sub ecx, eax
rep stosd
mov [timing_ptr], edi
%endif
cmp [esi], byte 0
jge near .trackloop
lodsb
%if USES_DELAY
cmp [esi], byte 0
jge near .delay
%endif
; Clamp and convert to shorts
fld1
mov edi, Clinkster_MusicBuffer
mov ecx, TOTAL_SAMPLES
add edi, dword [dword ebx + g_stereo]
.sloop:
fld dword [dword ebx + g_MixingBuffer + ecx*4]
fcomi st0, st1
fcmovnb st0, st1
fchs
fcomi st0, st1
fcmovnb st0, st1
fchs
fimul dword [c_finalamp]
fistp word [edi + ecx*4]
loop .sloop
fstp st0
ret 4
;; ********** Start music **********
section startmus text align=1
Clinkster_StartMusic:
_Clinkster_StartMusic@0:
; Start music
push byte 0
push byte 0
push byte 0
push _WaveFormat
push byte -1
push _WaveOutHandle
call [__imp__waveOutOpen@24]
push byte 32 ; sizeof(WAVEHDR)
push _WaveHdr
push dword [_WaveOutHandle] ; waveOutHandle
call [__imp__waveOutPrepareHeader@12]
push byte 32 ; sizeof(WAVEHDR)
push _WaveHdr
push dword [_WaveOutHandle]
call [__imp__waveOutWrite@12]
ret
;; ********** Get current play position **********
section getpos text align=1
Clinkster_GetPosition:
_Clinkster_GetPosition@0:
push byte 32 ; sizeof(MMTIME)
push _WaveTime
push dword [_WaveOutHandle]
call [__imp__waveOutGetPosition@12]
fild dword [_WaveTime+4]
%if CLINKSTER_TIMER_OFFSET>0
fiadd dword [c_timeoffset]
%endif
fidiv dword [c_ticklength]
ret
;; ********** Get time since instrument trigger **********
%if CLINKSTER_GENERATE_TIMING_DATA
section insttrig text align=1
Clinkster_GetInstrumentTrigger:
_Clinkster_GetInstrumentTrigger@8:
cvttss2si eax, [esp+8]
mov ecx, [esp+4]
shl ecx, LOGNUMTICKS+2
fld dword [esp+8]
fisub dword [Clinkster_NoteTiming+ecx+eax*4]
ret 8
%endif

View File

@ -0,0 +1,15 @@
; Implementation of float-to-integer conversion as needed by MSVC.
global __ftol2
global __ftol2_sse
section ftol text
__ftol2:
__ftol2_sse:
push eax
fisttp dword [esp]
pop eax
cdq
ret

View File

@ -0,0 +1,766 @@
; Clinkster music converted from examples/songs/Punqtured-Clinksterizer.xrns 2015-10-31 19:12:27
%define USES_SINE 1
%define USES_SAWTOOTH 1
%define USES_SQUARE 1
%define USES_PARABOLA 1
%define USES_TRIANGLE 1
%define USES_NOISE 1
%define USES_VELOCITY 1
%define USES_LONG_NOTES 0
%define USES_DELAY 1
%define USES_PANNING 0
%define USES_INDEXDECAY 1
%define USES_GAIN 1
%define SUBSAMPLES_PER_TICK 17404
%define MAX_INSTRUMENT_SUBSAMPLES 1638400
%define MAX_TOTAL_INSTRUMENT_SAMPLES 2555904
%define MAX_RELEASE_SUBSAMPLES 262144
%define TOTAL_SAMPLES 7077888
%define MAX_TRACK_INSTRUMENT_RENDERS 66
%define MAX_DELAY_LENGTH 13053
%define LEFT_DELAY_LENGTH 8687
%define RIGHT_DELAY_LENGTH 13053
%define DELAY_STRENGTH 0.53555790
%define NUMTRACKS 17
%define LOGNUMTICKS 11
%define MUSIC_LENGTH 1600
%define TICKS_PER_SECOND 10.13333333
section instdata data align=1
_InstrumentData:
; 00: Pizzicato Lead / 00|VST: Clinkster (Basic)
db 4,3,9,13,0,19,11,40,9,61,0,0,0,-50,-61,-24,-38,-3,-9
db 53,127,1,0,1,127,1,0,1,127,6,127,1,0,1,127,1,0,0,127,1,0,1,127,1,0,1,48,1,64,1,127,1,0,0,48,1,0,1,64,1,80,1,96,1,112,1,127,1,0,1,48,1,127,1,0,1,48,1,80,1,96,1,112,1,127,1,0,0,64,1,80,1,96,1,127,4,127,1,0,1,48,1,80,1,112,1,127,1,0,1,48,1,96,1,112,1,0,0,64,1,80,1,127,1,0,1,48,1,64,1,80,1,112,1,127,1,0,3,80,1,127,1,0,-1
; 01: Pizzicato Lead / 00|VST: Clinkster (Basic)
db 4,3,9,13,0,19,11,40,9,61,0,0,0,-50,-61,-24,-38,-3,-9
db 55,127,1,0,3,127,1,0,2,127,2,0,4,127,1,0,1,127,6,127,1,0,1,127,1,0,0,127,1,0,1,64,1,127,1,0,1,48,1,64,1,96,1,0,0,48,1,127,1,0,1,64,1,80,1,96,1,112,1,127,1,0,1,48,1,64,1,127,1,0,1,48,1,80,1,96,1,112,1,127,1,0,0,64,1,80,1,96,1,127,1,0,1,48,1,80,1,112,1,127,1,0,1,48,1,96,1,112,1,0,0,64,1,80,1,127,1,0,1,48,1,64,1,80,1,112,1,127,1,0,3,80,1,127,1,0,-1
; 02: Pizzicato Lead / 00|VST: Clinkster (Basic)
db 4,3,9,13,0,19,11,40,9,61,0,0,0,-50,-61,-24,-38,-3,-9
db 57,127,1,0,4,127,1,0,4,127,1,0,3,127,1,0,0,127,1,0,1,127,8,127,1,0,1,127,4,127,1,0,2,127,8,127,6,127,1,0,3,127,1,0,0,127,1,0,-1
; 03: Track 02 / 09|VST: Clinkster (Basic)
db 2,2,11,15,61,56,12,34,16,57,0,12,0,0,-67,-31,-17,-3,10
db 76,127,2,0,2,127,2,0,1,127,10,127,2,0,1,127,4,0,0,127,6,127,4,0,1,127,24,127,8,127,4,0,1,127,6,127,4,127,2,0,0,127,6,127,4,0,1,127,24,127,12,127,8,127,2,0,1,127,4,127,2,0,1,127,4,127,2,0,0,127,2,0,-1
; 04: Scream / 04|VST: Clinkster (Basic)
db 2,4,5,11,100,1,28,62,32,8,48,0,0,-20,-36,1,-3,-5,-27
db 19,127,20,0,11,127,20,0,1,127,14,0,-1
; 05: Scream / 04|VST: Clinkster (Basic)
db 2,4,5,11,100,1,28,62,32,8,48,0,0,-20,-36,1,-3,-5,-27
db 31,127,20,0,11,127,20,0,1,127,14,0,-1
; 06: Scream / 04|VST: Clinkster (Basic)
db 2,4,5,11,100,1,28,62,32,8,48,0,0,-20,-36,1,-3,-5,-27
db 43,127,20,0,11,127,16,0,-1
db -1
; 07: Strings / 06|VST: Clinkster (Basic)
db 1,2,2,6,94,42,25,13,18,30,0,0,0,0,-20,-3,-13,3,16
db 57,127,1,0,4,64,64,64,48,64,16,127,64,127,52,127,48,127,16,127,1,0,1,64,16,127,16,127,1,0,2,127,1,0,1,64,16,64,8,127,16,127,8,127,1,0,-1
; 08: Strings / 06|VST: Clinkster (Basic)
db 1,2,2,6,94,42,25,13,18,30,0,0,0,0,-20,-3,-13,3,16
db 59,127,32,127,20,127,16,127,1,0,0,64,64,64,16,127,64,127,16,127,1,0,6,127,48,127,1,0,3,64,16,127,8,127,1,0,-1
; 09: Strings / 06|VST: Clinkster (Basic)
db 1,2,2,6,94,42,25,13,18,30,0,0,0,0,-20,-3,-13,3,16
db 57,64,16,127,16,127,1,0,1,64,16,127,16,127,1,0,5,64,14,127,64,127,14,127,1,0,1,127,1,0,1,127,1,0,1,127,48,0,2,127,20,127,1,0,4,127,4,0,3,127,8,0,0,127,8,127,4,0,1,127,12,0,-1
; 10: Strings / 06|VST: Clinkster (Basic)
db 1,2,2,6,94,42,25,13,18,30,0,0,0,0,-20,-3,-13,3,16
db 67,64,72,64,56,64,24,80,8,127,72,127,60,127,56,127,24,127,1,0,-1
; 11: Long Bass / 01|VST: Clinkster (Basic)
db 0,3,4,2,86,55,17,64,-30,30,0,12,0,-65,-20,3,-10,16,3
db 29,96,16,127,16,0,1,96,32,127,52,127,32,127,24,0,1,96,16,127,16,0,1,96,16,127,16,0,0,96,16,127,16,0,-1
; 12: Bassdrum / 02|VST: Clinkster (Basic)
db 0,4,0,0,11,2,49,0,0,30,42,43,-70,-70,-49,1,-59,2,-27
db 12,127,1,0,11,64,1,96,1,127,1,0,11,127,1,0,-1
; 13: Zap / 03|VST: Clinkster (Basic)
db 0,0,5,9,100,80,3,77,9,30,-12,36,-47,-70,-67,-27,-59,-9,-11
db 30,127,1,0,6,127,1,0,4,64,1,80,1,0,6,48,1,0,4,32,1,0,-1
; 14: Counter Bass / 05|VST: Clinkster (Basic)
db 0,3,0,3,6,19,2,34,1,30,0,-12,0,0,-39,-13,-15,1,3
db 41,48,2,127,2,0,1,48,2,64,2,80,2,96,2,112,2,127,10,127,2,0,1,48,2,127,2,0,1,48,2,127,2,0,0,48,2,127,2,0,-1
; 15: Snare / 07|snare1
db 0,5,26,10,10,16,20,4,-2,30,-2,0,-70,0,28,53,-104,-4,1
db 55,3,2,4,2,7,2,16,1,17,2,20,2,30,2,32,1,34,2,43,2,47,2,48,1,56,2,57,2,60,2,64,2,64,1,69,2,80,1,82,2,96,2,96,1,112,1,127,2,127,1,0,4,16,2,19,2,22,2,25,2,28,2,31,2,34,2,37,2,40,2,43,2,46,2,50,2,53,2,56,2,59,2,62,2,65,2,68,2,71,2,74,2,77,2,81,2,84,2,87,2,90,1,91,1,92,1,94,1,95,1,97,1,98,1,100,1,101,1,103,1,104,1,106,1,107,1,109,1,110,1,112,5,112,1,0,-1
; 16: Hihat / 08|hihat
db 0,0,13,23,0,7,12,37,2,30,55,22,0,0,0,6,-59,-6,-19
db 68,80,2,127,2,0,-1
db -1,-1
section notepos data align=1
_NotePositions:
; 00: Pizzicato Lead / 00|VST: Clinkster (Basic)
; position 0 - pattern 0
db 0,6,1,1,13,1,1,1,2,4,5,1,1,1,1,1,1,1,1,1,8,2,1,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,2,2,2,2,2,2,2,2,1,1,1,1
; position 1 - pattern 1
db 16,6,1,1,13,1,1,1,2,4,5,1,1,1,1,1,1,1,1,1,8,2,1,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,2,2,2,2,2,2,2,2,1,1,1,1
; position 4 - pattern 4
db -2,16,4,6,6,4,6,6,4,4,2,2,4,4,2,4,2,4,4,6,2,2,2,4,4,2,2,4,4,6,2,2,2,4,4,2,2
; position 5 - pattern 5
db 4,4,6,6,4,6,6,4,4,2,2,4,4,2,4,2,4,4,6,2,2,2,4,4,2,2,4,4,6,2,2,2
; position 6 - pattern 6
db 16,6,1,1,13,1,1,1,2,4,5,1,1,1,1,1,1,1,1,1,8,2,1,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,2,2,2,2,2,2,2,2,1,1,1,1,8,1,1,1,1,1,1,1
; position 7 - pattern 7
db 1,6,1,1,13,1,1,1,2,4,5,1,1,1,1,1,1,1,1,1,4,6,6,4
; position 8 - pattern 8
db 64,4,6,6,4,6,6,4,4,2,2,4,4,2,4,2,4,4,6,2,2,2,4,4,2,2,4,4,6,2,2,2,4,4,2,2
; position 9 - pattern 9
db 4,4,6,6,4,6,6,4,4,2,2,4,4,2,4,2,4,4,6,2,2,2,4,4,2,2,4,4,6,2,2,2,4,4,2,2
; position 10 - pattern 10
db 4,4,6,6,4,6,6,4,4,2,2,4,4,2,4,2,4,4,6,2,2,2,4,4,2,2,4,4,6,2,2,2,4,4,2,2
; position 11 - pattern 11
db 4,4,6,6,4,6,6,4,4,2,2,4,4,2,4,2,4,4,6,2,2,2,4,4,2,2,4,4,6,2,2,2
; 01: Pizzicato Lead / 00|VST: Clinkster (Basic)
; position 0 - pattern 0
db 0,6,1,1,13,1,1,1,2,4,5,1,1,1,1,1,1,1,1,1,8,2,1,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,2,2,2,2,2,2,2,2,1,1,1,1
; position 1 - pattern 1
db 16,6,1,1,13,1,1,1,2,4,5,1,1,1,1,1,1,1,1,1,8,2,1,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,2,2,2,2,2,2,2,2,1,1,1,1
; position 4 - pattern 4
db -2,16,4,6,6,4,6,6,4,6,2,4,4,2,4,2,2,2,4,6,4,2,4,4,2,2,4,4,6,4,2,4,4,2,2
; position 5 - pattern 5
db 4,4,2,1,1,2,6,4,6,6,4,6,2,3,1,4,2,4,2,2,2,4,6,4,2,4,4,2,2,4,4,6,4,2
; position 6 - pattern 6
db 16,6,1,1,13,1,1,1,2,4,5,1,1,1,1,1,1,1,1,1,8,2,1,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,2,2,2,2,2,2,2,2,1,1,1,1,8,1,1,1,1,1,1,1
; position 7 - pattern 7
db 1,6,1,1,13,1,1,1,2,4,5,1,1,1,1,1,1,1,1,1,4,6,6,4
; position 8 - pattern 8
db 64,4,6,6,4,6,6,4,6,2,4,4,2,4,2,2,2,4,6,4,2,4,4,2,2,4,4,6,4,2,4,4,2,2
; position 9 - pattern 9
db 4,4,6,6,4,6,6,4,6,2,4,4,2,4,2,2,2,4,6,4,2,4,4,2,2,4,4,6,4,2,4,4,2,2
; position 10 - pattern 10
db 4,4,6,6,4,6,6,4,6,2,4,4,2,4,2,2,2,4,6,4,2,4,4,2,2,4,4,6,4,2,4,4,2,2
; position 11 - pattern 11
db 4,4,6,6,4,6,6,4,6,2,4,4,2,4,2,2,2,4,6,4,2,4,4,2,2,4,4,6,2,2,2
; 02: Pizzicato Lead / 00|VST: Clinkster (Basic)
; position 4 - pattern 4
db -3,8,4,4,6,6,2,4,4,8,2,6,6,4,8,8,8,8,8,8,8
; position 5 - pattern 5
db 16,4,4,6,6,2,4,4,8,2,6,6,4,8,8,8,8,8,8
; position 8 - pattern 8
db -2,24,4,4,6,6,2,4,4,8,2,6,6,4,8,8,8,8,8,8,8
; position 9 - pattern 9
db 16,4,4,6,6,2,4,4,8,2,6,6,4,8,8,8,8,8,8,8
; position 10 - pattern 10
db 16,4,4,6,6,2,4,4,8,2,6,6,4,8,8,8,8,8,8,8
; position 11 - pattern 11
db 16,4,4,6,6,2,4,4,8,2,6,6,4,8,8,8,8,8,8
; 03: Track 02 / 09|VST: Clinkster (Basic)
; position 4 - pattern 4
db -3,0,2,4,4,2,2,2,2,4,4,4,6,2,4,4,6,4,4,4,8,4,12,8,4
; position 5 - pattern 5
db 28,2,4,4,2,2,2,2,4,4,4,6,2,4,4,6,4,4,4,8,4,12,8,4
; position 8 - pattern 8
db -2,28,10,2,2,2,4,2,6,4,6,6,4,6,6,4
; position 9 - pattern 9
db 64,10,2,2,2,4,2,6,4,6,6,4,6,6,4
; position 10 - pattern 10
db 64,10,2,2,2,4,2,6,4,6,6,4,6,6,4
; position 11 - pattern 11
db 64,10,2,2,2,4,2,6,4,6,6,4,6,6,4
; 04: Scream / 04|VST: Clinkster (Basic)
; position 0 - pattern 0
db 60
; position 1 - pattern 1
db -1,128,36
; position 3 - pattern 3
db -2,0
; position 4 - pattern 4
db 92
; position 6 - pattern 6
db -1,196,60
; position 7 - pattern 7
db 68,60
; position 8 - pattern 8
db -1,128
; position 9 - pattern 9
db -1,128
; position 10 - pattern 10
db 68,60
; position 11 - pattern 11
db 68,60
; 05: Scream / 04|VST: Clinkster (Basic)
; position 0 - pattern 0
db 62
; position 1 - pattern 1
db -1,128,36
; position 3 - pattern 3
db -2,0
; position 4 - pattern 4
db 92
; position 6 - pattern 6
db -1,196,60
; position 7 - pattern 7
db 68,60
; position 8 - pattern 8
db -1,128
; position 9 - pattern 9
db -1,128
; position 10 - pattern 10
db 68,60
; position 11 - pattern 11
db 68,60
; 06: Scream / 04|VST: Clinkster (Basic)
; position 1 - pattern 1
db -1,232
; position 4 - pattern 4
db -2,96
; position 8 - pattern 8
db -3,0
; position 9 - pattern 9
db -1,128
; 07: Strings / 06|VST: Clinkster (Basic)
; position 0 - pattern 0
db 0,16,16,16,16
; position 1 - pattern 1
db 64,16,16,16,16
; position 2 - pattern 2
db 64,16,16,16,16
; position 3 - pattern 3
db 64,16,16,16,16
; position 4 - pattern 4
db 64,16,16,16,16
; position 5 - pattern 5
db 64,16,16,16,16
; position 6 - pattern 6
db 64,16,16,16,16
; position 7 - pattern 7
db 64,16,16,16,6,6,4
; position 10 - pattern 10
db -2,64,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6
; position 11 - pattern 11
db 6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6
; 08: Strings / 06|VST: Clinkster (Basic)
; position 0 - pattern 0
db 0,16,16,64
; position 1 - pattern 1
db 32,16,16,64
; position 2 - pattern 2
db 32,16,16,64
; position 3 - pattern 3
db 32,16,16,64
; position 4 - pattern 4
db 32,16,16,64
; position 5 - pattern 5
db 32,16,16,64
; position 6 - pattern 6
db 32,16,16,64
; position 7 - pattern 7
db 32,16,16,16,6,6,4
; position 10 - pattern 10
db -2,64,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6
; position 11 - pattern 11
db 6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6
; 09: Strings / 06|VST: Clinkster (Basic)
; position 0 - pattern 0
db 0,16,32
; position 1 - pattern 1
db 80,16,32
; position 2 - pattern 2
db 80,16,32,24,8,8,4
; position 3 - pattern 3
db 36,16,32,24,8,8,4
; position 4 - pattern 4
db 36,16,32
; position 5 - pattern 5
db 80,16,32
; position 6 - pattern 6
db 80,16,32
; position 7 - pattern 7
db 80,16,32,6,6,4
; position 10 - pattern 10
db -2,64,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6
; position 11 - pattern 11
db 6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6,4,6,6
; 10: Strings / 06|VST: Clinkster (Basic)
; position 0 - pattern 0
db 24,32
; position 1 - pattern 1
db 96,32
; position 2 - pattern 2
db 96,32
; position 3 - pattern 3
db 96,32
; position 4 - pattern 4
db 96,8,24
; position 5 - pattern 5
db 96,8,24
; position 6 - pattern 6
db 96,32
; position 7 - pattern 7
db 96
; position 10 - pattern 10
db -2,128,32
; position 11 - pattern 11
db 96,32
; 11: Long Bass / 01|VST: Clinkster (Basic)
; position 0 - pattern 0
db 64
; position 1 - pattern 1
db 64,16,16,16,16
; position 2 - pattern 2
db 64,16,16,16,16,32
; position 3 - pattern 3
db 32,16,16,16,16,32
; position 4 - pattern 4
db 96
; position 5 - pattern 5
db -1,128
; position 6 - pattern 6
db 64,16,16,16,16,32
; position 7 - pattern 7
db 32,16,16,16,16,32
; position 9 - pattern 9
db -1,224,32
; position 10 - pattern 10
db 32,16,16,16,16,32
; position 11 - pattern 11
db 32,16,16,16,16,32
; 12: Bassdrum / 02|VST: Clinkster (Basic)
; position 2 - pattern 2
db -2,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 3 - pattern 3
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 5 - pattern 5
db -2,16,13,1
; position 6 - pattern 6
db 2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 7 - pattern 7
db 4,4,4,4,4,4,4,4,4,4,4,4,4,6,6,4,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 10 - pattern 10
db -2,32,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 11 - pattern 11
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,1,1
; 13: Zap / 03|VST: Clinkster (Basic)
; position 2 - pattern 2
db -2,1,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1
; position 3 - pattern 3
db 3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1,4,3,1,2,1,3,1,1
; 14: Counter Bass / 05|VST: Clinkster (Basic)
; position 1 - pattern 1
db -1,130,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 2 - pattern 2
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 3 - pattern 3
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 4 - pattern 4
db 36,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 5 - pattern 5
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 6 - pattern 6
db 20,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 7 - pattern 7
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 8 - pattern 8
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 9 - pattern 9
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 10 - pattern 10
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 11 - pattern 11
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; 15: Snare / 07|snare1
; position 1 - pattern 1
db -1,132,8,8,8,8,8,8,8,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 2 - pattern 2
db 5,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
; position 3 - pattern 3
db 8,8,8,8,8,8,8,8,8,8,8,8,4
; position 5 - pattern 5
db -1,224,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,12,2,1
; position 6 - pattern 6
db 5,2,6,8,3,2,3,8,8,3,5,2,6,8,3,5,8,3,5,8,8,8,8,1,2
; position 7 - pattern 7
db 5,2,6,8,3,2,3,8,8,3,1,6,6,4,4,3,5,8,3,5
; position 9 - pattern 9
db -1,228,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 10 - pattern 10
db 5,3,5,8,3,2,3,8,3,5,8,3,2,3,2,1,5,3,5,8,3,2,3,8,3,5,8,3,2,3,2,1
; position 11 - pattern 11
db 5,3,5,8,3,2,3,8,3,5,8,3,2,3,2,1
; 16: Hihat / 08|hihat
; position 1 - pattern 1
db -1,130,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 2 - pattern 2
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 3 - pattern 3
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2
; position 5 - pattern 5
db -1,162,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 6 - pattern 6
db 20,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 7 - pattern 7
db 4,4,4,4,4,4,4,4,4,4,4,4,2,6,6,4
; position 8 - pattern 8
db 66,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 9 - pattern 9
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 10 - pattern 10
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
; position 11 - pattern 11
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
section notesamp data align=1
_NoteSamples:
; 00: Pizzicato Lead / 00|VST: Clinkster (Basic)
; position 0 - pattern 0
db 2,16,24,34,42,32,31,18,6,6,41,33,24,11,7,13,23,14,8,27,37,10,23,36,27,5,27,44,41,29,18,30,40,28,21,29,38,30,19,28,41,43,38,29,20,14,6,31,22,42,31,22,31,22,15,39,29,20,14,6
; position 1 - pattern 1
db 2,16,24,34,42,32,31,18,6,6,41,33,24,11,7,13,23,14,8,27,37,10,23,36,27,5,27,44,41,29,18,30,40,28,21,29,38,30,19,28,41,43,38,29,20,14,6,31,22,42,31,22,31,22,15,39,29,20,14,6
; position 4 - pattern 4
db 3,3,3,6,4,4,9,9,5,5,5,27,0,27,0,26,6,1,1,27,31,15,1,6,1,15,6,1,1,27,31,15,1,6,1,15
; position 5 - pattern 5
db 3,3,3,6,4,4,9,9,5,5,5,27,0,27,0,26,6,1,1,27,31,15,1,6,1,15,6,1,1,27,31,15
; position 6 - pattern 6
db 2,16,24,34,42,32,31,18,6,6,41,33,24,11,7,13,23,14,8,27,37,10,23,36,27,5,27,44,41,29,18,30,40,28,21,29,38,30,19,28,41,43,38,29,20,14,6,31,22,42,31,22,31,22,15,39,29,20,14,6,38,35,29,25,21,11,21,12
; position 7 - pattern 7
db 2,16,24,34,42,32,31,18,6,6,41,33,24,11,7,13,23,14,8,27,17,27,17,22
; position 8 - pattern 8
db 3,3,3,6,4,4,9,9,5,5,5,27,0,27,0,26,6,1,1,27,31,15,1,6,1,15,6,1,1,27,31,15,1,6,1,15
; position 9 - pattern 9
db 3,3,3,6,4,4,9,9,5,5,5,27,0,27,0,26,6,1,1,27,31,15,1,6,1,15,6,1,1,27,31,15,1,6,1,15
; position 10 - pattern 10
db 3,3,3,6,4,4,9,9,5,5,5,27,0,27,0,26,6,1,1,27,31,15,1,6,1,15,6,1,1,27,31,15,1,6,1,15
; position 11 - pattern 11
db 3,3,3,6,4,4,9,9,5,5,5,27,0,27,0,26,6,1,1,27,31,15,1,6,1,15,6,1,42,31,22,15
db -1
; 01: Pizzicato Lead / 00|VST: Clinkster (Basic)
; position 0 - pattern 0
db 4,20,29,38,46,36,35,23,9,9,45,37,29,15,10,17,28,18,11,31,41,13,28,40,31,7,31,48,45,33,23,34,44,32,26,33,42,34,24,32,45,47,42,33,25,18,9,35,27,46,35,27,35,27,19,43,33,25,18,9
; position 1 - pattern 1
db 4,20,29,38,46,36,35,23,9,9,45,37,29,15,10,17,28,18,11,31,41,13,28,40,31,7,31,48,45,33,23,34,44,32,26,33,42,34,24,32,45,47,42,33,25,18,9,35,27,46,35,27,35,27,19,43,33,25,18,9
; position 4 - pattern 4
db 5,22,22,6,9,27,7,31,31,19,14,14,14,14,19,22,7,19,19,6,9,19,3,35,9,7,19,19,6,9,19,3,35,9
; position 5 - pattern 5
db 5,22,21,12,5,22,6,9,27,7,31,31,19,8,14,14,14,14,19,22,7,19,19,6,9,19,3,35,9,7,19,19,6,9
; position 6 - pattern 6
db 4,20,29,38,46,36,35,23,9,9,45,37,29,15,10,17,28,18,11,31,41,13,28,40,31,7,31,48,45,33,23,34,44,32,26,33,42,34,24,32,45,47,42,33,25,18,9,35,27,46,35,27,35,27,19,43,33,25,18,9,42,39,33,30,26,15,26,16
; position 7 - pattern 7
db 4,20,29,38,46,36,35,23,9,9,45,37,29,15,10,17,28,18,11,31,14,14,14,19
; position 8 - pattern 8
db 5,22,22,6,9,27,7,31,31,19,14,14,14,14,19,22,7,19,19,6,9,19,3,35,9,7,19,19,6,9,19,3,35,9
; position 9 - pattern 9
db 5,22,22,6,9,27,7,31,31,19,14,14,14,14,19,22,7,19,19,6,9,19,3,35,9,7,19,19,6,9,19,3,35,9
; position 10 - pattern 10
db 5,22,22,6,9,27,7,31,31,19,14,14,14,14,19,22,7,19,19,6,9,19,3,35,9,7,19,19,6,9,19,3,35,9
; position 11 - pattern 11
db 5,22,22,6,9,27,7,31,31,19,14,14,14,14,19,22,7,19,19,6,9,19,3,35,9,7,19,3,2,1,0
db -1
; 02: Pizzicato Lead / 00|VST: Clinkster (Basic)
; position 4 - pattern 4
db 0,4,12,2,1,3,11,2,8,11,10,7,5,12,12,13,9,6,3,2
; position 5 - pattern 5
db 0,4,12,2,1,3,11,2,8,11,10,7,5,12,12,13,9,6,3
; position 8 - pattern 8
db 0,4,12,2,1,3,11,2,8,11,10,7,5,12,12,13,9,6,3,2
; position 9 - pattern 9
db 0,4,12,2,1,3,11,2,8,11,10,7,5,12,12,13,9,6,3,2
; position 10 - pattern 10
db 0,4,12,2,1,3,11,2,8,11,10,7,5,12,12,13,9,6,3,2
; position 11 - pattern 11
db 0,4,12,2,1,3,11,2,8,11,10,7,5,12,12,13,9,6,11
db -1
; 03: Track 02 / 09|VST: Clinkster (Basic)
; position 4 - pattern 4
db 20,11,19,23,22,18,12,9,6,9,13,12,9,6,13,11,9,6,8,19,16,17,21,17
; position 5 - pattern 5
db 20,11,19,23,22,18,12,9,6,9,13,12,9,6,13,11,9,6,8,19,16,17,21,17
; position 8 - pattern 8
db 2,0,1,3,4,1,5,4,5,10,6,13,10,14,7
; position 9 - pattern 9
db 2,0,1,3,4,1,5,4,5,10,6,13,10,14,15
; position 10 - pattern 10
db 2,0,1,3,4,1,5,4,5,10,6,13,10,14,7
; position 11 - pattern 11
db 2,0,1,3,4,1,5,4,5,10,6,13,10,14,15
db -1
; 04: Scream / 04|VST: Clinkster (Basic)
; position 0 - pattern 0
db 1
; position 1 - pattern 1
db 1,1
; position 3 - pattern 3
db 0
; position 4 - pattern 4
db 1
; position 6 - pattern 6
db 2,1
; position 7 - pattern 7
db 2,1
; position 8 - pattern 8
db 1
; position 9 - pattern 9
db 1
; position 10 - pattern 10
db 2,1
; position 11 - pattern 11
db 2,1
db -1
; 05: Scream / 04|VST: Clinkster (Basic)
; position 0 - pattern 0
db 1
; position 1 - pattern 1
db 1,1
; position 3 - pattern 3
db 0
; position 4 - pattern 4
db 1
; position 6 - pattern 6
db 2,1
; position 7 - pattern 7
db 2,1
; position 8 - pattern 8
db 1
; position 9 - pattern 9
db 1
; position 10 - pattern 10
db 2,1
; position 11 - pattern 11
db 2,1
db -1
; 06: Scream / 04|VST: Clinkster (Basic)
; position 1 - pattern 1
db 1
; position 4 - pattern 4
db 0
; position 8 - pattern 8
db 0
; position 9 - pattern 9
db 0
db -1
; 07: Strings / 06|VST: Clinkster (Basic)
; position 0 - pattern 0
db 15,7,10,16,6
; position 1 - pattern 1
db 15,7,10,16,4
; position 2 - pattern 2
db 15,7,10,16,4
; position 3 - pattern 3
db 15,7,10,16,5
; position 4 - pattern 4
db 13,3,9,14,1
; position 5 - pattern 5
db 13,3,9,14,2
; position 6 - pattern 6
db 15,7,10,16,4
; position 7 - pattern 7
db 15,7,10,17,12,17,6
; position 10 - pattern 10
db 17,17,0,8,8,8,11,11,11,17,17,17,8,8,8,8,8,8,8,8,8,8,8,8
; position 11 - pattern 11
db 17,17,0,8,8,8,11,11,11,17,17,17,8,8,8,8,8,8,8
db -1
; 08: Strings / 06|VST: Clinkster (Basic)
; position 0 - pattern 0
db 7,12,6,2
; position 1 - pattern 1
db 7,12,6,0
; position 2 - pattern 2
db 7,12,6,0
; position 3 - pattern 3
db 7,12,6,1
; position 4 - pattern 4
db 5,11,4,0
; position 5 - pattern 5
db 5,11,4,2
; position 6 - pattern 6
db 7,12,6,0
; position 7 - pattern 7
db 7,12,7,8,8,8,9
; position 10 - pattern 10
db 8,8,8,13,13,13,8,8,8,8,8,8,3,3,3,3,3,3,10,10,10,10,10,10
; position 11 - pattern 11
db 8,8,8,13,13,13,8,8,8,8,8,8,3,3,3,3,3,3,10
db -1
; 09: Strings / 06|VST: Clinkster (Basic)
; position 0 - pattern 0
db 1,4,7
; position 1 - pattern 1
db 1,4,8
; position 2 - pattern 2
db 1,4,8,17,16,15,13
; position 3 - pattern 3
db 1,4,8,17,16,18,19
; position 4 - pattern 4
db 0,3,6
; position 5 - pattern 5
db 0,3,6
; position 6 - pattern 6
db 1,4,8
; position 7 - pattern 7
db 1,4,9,9,9,12
; position 10 - pattern 10
db 2,2,11,5,5,5,10,10,10,9,9,9,10,10,10,10,10,10,14,14,14,14,14,14
; position 11 - pattern 11
db 2,2,11,5,5,5,10,10,10,9,9,9,10,10,10,10,10,10,14
db -1
; 10: Strings / 06|VST: Clinkster (Basic)
; position 0 - pattern 0
db 7,6
; position 1 - pattern 1
db 7,4
; position 2 - pattern 2
db 7,4
; position 3 - pattern 3
db 7,5
; position 4 - pattern 4
db 3,2,0
; position 5 - pattern 5
db 3,2,1
; position 6 - pattern 6
db 7,4
; position 7 - pattern 7
db 7
; position 10 - pattern 10
db 8,4
; position 11 - pattern 11
db 8,8
db -1
; 11: Long Bass / 01|VST: Clinkster (Basic)
; position 0 - pattern 0
db 3
; position 1 - pattern 1
db 7,9,11,1,3
; position 2 - pattern 2
db 6,8,10,0,2,2
; position 3 - pattern 3
db 7,9,11,1,4,5
; position 4 - pattern 4
db 3
; position 5 - pattern 5
db 3
; position 6 - pattern 6
db 7,9,11,1,4,4
; position 7 - pattern 7
db 7,9,11,1,4,4
; position 9 - pattern 9
db 4,4
; position 10 - pattern 10
db 7,9,11,1,4,4
; position 11 - pattern 11
db 7,9,11,1,4,5
db -1
; 12: Bassdrum / 02|VST: Clinkster (Basic)
; position 2 - pattern 2
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; position 3 - pattern 3
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; position 5 - pattern 5
db 3,4,3
; position 6 - pattern 6
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; position 7 - pattern 7
db 3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; position 10 - pattern 10
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; position 11 - pattern 11
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,2,3
db -1
; 13: Zap / 03|VST: Clinkster (Basic)
; position 2 - pattern 2
db 5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1
; position 3 - pattern 3
db 5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2,0,4,2,3,1,5,4,2
db -1
; 14: Counter Bass / 05|VST: Clinkster (Basic)
; position 1 - pattern 1
db 10,10,10,10,12,12,12,12,14,14,14,14,1,1,1,1,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
; position 2 - pattern 2
db 10,10,10,10,12,12,12,12,14,14,14,14,1,1,1,1,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
; position 3 - pattern 3
db 10,10,10,10,12,12,12,12,14,14,14,14,1,1,1,1,8,8,8,8,8,8,8,7
; position 4 - pattern 4
db 9,9,9,9,11,11,11,11,13,13,13,13,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
; position 5 - pattern 5
db 9,9,9,9,11,11,11,11,13,13,13,13,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2
; position 6 - pattern 6
db 10,10,10,10,12,12,12,12,14,14,14,14,1,1,1,1,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
; position 7 - pattern 7
db 10,10,10,10,12,12,12,12,14,14,14,14,1,1,1,1,8,8,8,8,8,8,8,8,6,5,4,3,2,2,2,2
; position 8 - pattern 8
db 9,9,9,9,11,11,11,11,13,13,13,13,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
; position 9 - pattern 9
db 9,9,9,9,11,11,11,11,13,13,13,13,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
; position 10 - pattern 10
db 9,9,9,9,11,11,11,11,13,13,13,13,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
; position 11 - pattern 11
db 9,9,9,9,11,11,11,11,13,13,13,13,0,0,0,0,2,2,2,2,2,2,2,2
db -1
; 15: Snare / 07|snare1
; position 1 - pattern 1
db 3,3,7,7,11,11,16,16,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64
; position 2 - pattern 2
db 24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24
; position 3 - pattern 3
db 24,24,24,24,24,24,24,24,24,24,24,24,24
; position 5 - pattern 5
db 1,0,4,2,6,5,9,8,12,10,17,14,19,13,20,15,24,18,24,18,24,18,24,18,24,18,24,18,24,18,24,21,24,23,24,22
; position 6 - pattern 6
db 24,7,24,24,7,7,24,24,24,7,24,7,24,24,7,24,24,7,24,24,24,24,24,18,18
; position 7 - pattern 7
db 24,7,24,24,7,7,24,24,24,7,24,24,24,24,24,7,24,24,7,24
; position 9 - pattern 9
db 25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,65
; position 10 - pattern 10
db 24,18,24,24,18,16,24,24,16,24,24,18,18,24,18,16,24,18,24,24,18,16,24,24,16,24,24,18,18,24,18,16
; position 11 - pattern 11
db 24,18,24,24,18,16,24,24,16,24,24,18,18,24,18,16
db -1
; 16: Hihat / 08|hihat
; position 1 - pattern 1
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 2 - pattern 2
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 3 - pattern 3
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 5 - pattern 5
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 6 - pattern 6
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 7 - pattern 7
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 8 - pattern 8
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
; position 9 - pattern 9
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
; position 10 - pattern 10
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
; position 11 - pattern 11
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
db -1

View File

@ -0,0 +1,6 @@
Player source code and usage example.
In order to build, you need to set up a rule in Visual Studio to compile
.asm files. I can recommend Yasm, which has nice Visual Studio integration
plus debugging support, but Nasm works as well.