1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
# Minimal Audio Tracker
in addition to being able to generate spectrograms (aka "Samples") on the
fly and play them at once, we need a way to assemble samples (assets or
generated) into modifiable patterns and loops. Like what the Trackers were
doing in the old time.
## The idea
A script can take a 'tracker music' text file that describes the sequence
of samples (a 'pattern') in a first part. In a second part, these sequence
a laid out with a timestamp (potentially overlapping) to generate the full
music score.
The patterns' samples (spectrograms) are not yet generated, it's just the
'musical score' here. We still need to 'play' the score but with modifiers
applied.
### Modifiers
For diversity, these essential patterns can be modified on the fly before
being generated as spectrogram on the fly.
Modifiers could be:
* randomize (for drums, e.g.)
* add accents and stresses
* modulate volume
* add distortion or noise
* add 'grain' and static noise
* flanger, vocoding, robotic voice, etc.
* etc.
These modifiers are applied to a predefined pattern just before it's
generated for playback (or assembling in the final track).
### How would that work in practice?
The musical score is a text file, that a tool will convert to run-time
code to be compiled in the final binary demo64k.
This generated code can be mixed with fixed code from the demo codebase
itself (explosion predefined at a given time ,etc.)
The baking is done at compile time, and the code will go in src/generated/
## .track File Format
### Timing System
**Unit-less Timing Convention:**
- All time values are **unit-less** (not beats or seconds)
- Convention: **1 unit = 4 beats**
- Conversion to seconds: `seconds = units * (4 / BPM) * 60`
- At 120 BPM: 1 unit = 2 seconds
This makes patterns independent of BPM - changing BPM only affects playback speed, not pattern structure.
### File Structure
```
# Comments start with #
BPM <tempo> # Optional, defaults to 120 BPM
SAMPLE <name> # Define sample (asset or generated note)
PATTERN <name> LENGTH <duration> # Define pattern with unit-less duration
<unit_time>, <sample>, <volume>, <pan> # Pattern events
SCORE # Score section (pattern triggers)
<unit_time>, <pattern_name>
```
### Examples
#### Simple 4-beat pattern (1 unit):
```
PATTERN kick_snare LENGTH 1.0
0.00, ASSET_KICK_1, 1.0, 0.0 # Start of pattern (beat 0)
0.25, ASSET_SNARE_1, 0.9, 0.0 # 1/4 through (beat 1)
0.50, ASSET_KICK_1, 1.0, 0.0 # 1/2 through (beat 2)
0.75, ASSET_SNARE_1, 0.9, 0.0 # 3/4 through (beat 3)
```
#### Score triggers:
```
SCORE
0.0, kick_snare # Trigger at 0 seconds (120 BPM)
1.0, kick_snare # Trigger at 2 seconds (1 unit = 2s at 120 BPM)
2.0, kick_snare # Trigger at 4 seconds
```
#### Generated note:
```
SAMPLE NOTE_C4 # Automatically generates C4 note (261.63 Hz)
PATTERN melody LENGTH 1.0
0.00, NOTE_C4, 0.8, 0.0
0.25, NOTE_E4, 0.7, 0.0
0.50, NOTE_G4, 0.8, 0.0
```
### Conversion Reference
At 120 BPM (1 unit = 4 beats = 2 seconds):
| Units | Beats | Seconds | Description |
|-------|-------|---------|-------------|
| 0.00 | 0 | 0.0 | Start |
| 0.25 | 1 | 0.5 | Quarter |
| 0.50 | 2 | 1.0 | Half |
| 0.75 | 3 | 1.5 | Three-quarter |
| 1.00 | 4 | 2.0 | Full pattern |
### Pattern Length
- `LENGTH` parameter is optional, defaults to 1.0
- Can be any value (0.5 for half-length, 2.0 for double-length, etc.)
- Events must be within range `[0.0, LENGTH]`
Example of half-length pattern:
```
PATTERN short_fill LENGTH 0.5 # 2 beats = 1 second at 120 BPM
0.00, ASSET_HIHAT, 0.7, 0.0
0.50, ASSET_HIHAT, 0.6, 0.0 # 0.50 * 0.5 = 1 beat into the pattern
```
|