@@ -2,6 +2,9 @@ unit Song; |
| 2 |
|
2 |
|
| 3 |
{$mode delphi} |
3 |
{$mode delphi} |
| 4 |
|
4 |
|
|
|
5 |
// TODO: Lots of duplicated code in here. At some point this should switch over |
|
|
6 |
// to a more parsable format, like NBT, JSON, or XML or something. |
|
|
7 |
|
| 5 |
interface |
8 |
interface |
| 6 |
|
9 |
|
| 7 |
uses Classes, HugeDatatypes, instruments, Constants; |
10 |
uses Classes, HugeDatatypes, instruments, Constants; |
@@ -41,9 +44,28 @@ type |
| 41 |
Routines: TRoutineBank; |
44 |
Routines: TRoutineBank; |
| 42 |
end; |
45 |
end; |
| 43 |
|
46 |
|
|
|
47 |
TSongV3 = packed record |
|
|
48 |
Version: Integer; |
|
|
49 |
|
|
|
50 |
Name: ShortString; |
|
|
51 |
Artist: ShortString; |
|
|
52 |
Comment: ShortString; |
|
|
53 |
|
|
|
54 |
Instruments: TInstrumentCollection; |
|
|
55 |
|
|
|
56 |
Waves: TWaveBank; |
|
|
57 |
|
|
|
58 |
TicksPerRow: Integer; |
|
|
59 |
|
|
|
60 |
Patterns: TPatternMap; |
|
|
61 |
OrderMatrix: TOrderMatrix; |
|
|
62 |
|
|
|
63 |
Routines: TRoutineBank; |
|
|
64 |
end; |
|
|
65 |
|
| 44 |
{ TSong } |
66 |
{ TSong } |
| 45 |
|
67 |
|
| 46 |
TSong = TSongV2; |
68 |
TSong = TSongV3; |
| 47 |
|
69 |
|
| 48 |
procedure WriteSongToStream(S: TStream; const ASong: TSong); |
70 |
procedure WriteSongToStream(S: TStream; const ASong: TSong); |
| 49 |
procedure ReadSongFromStream(S: TStream; out ASong: TSong); |
71 |
procedure ReadSongFromStream(S: TStream; out ASong: TSong); |
@@ -52,6 +74,7 @@ procedure DestroySong(var S: TSong); |
| 52 |
|
74 |
|
| 53 |
function UpgradeSong(S: TSongV1): TSong; overload; |
75 |
function UpgradeSong(S: TSongV1): TSong; overload; |
| 54 |
function UpgradeSong(S: TSongV2): TSong; overload; |
76 |
function UpgradeSong(S: TSongV2): TSong; overload; |
|
|
77 |
function UpgradeSong(S: TSongV3): TSong; overload; |
| 55 |
|
78 |
|
| 56 |
implementation |
79 |
implementation |
| 57 |
|
80 |
|
@@ -67,8 +90,6 @@ begin |
| 67 |
n := SizeOf(TSongV1) - SizeOf(TPatternMap) - SizeOf(TOrderMatrix); |
90 |
n := SizeOf(TSongV1) - SizeOf(TPatternMap) - SizeOf(TOrderMatrix); |
| 68 |
S.Read(ASong, n); |
91 |
S.Read(ASong, n); |
| 69 |
|
92 |
|
| 70 |
// ASong.Patterns.Clear; |
|
|
| 71 |
|
|
|
| 72 |
// Create the patterns |
93 |
// Create the patterns |
| 73 |
ASong.Patterns := TPatternMap.Create; |
94 |
ASong.Patterns := TPatternMap.Create; |
| 74 |
// Read the pattern count |
95 |
// Read the pattern count |
@@ -107,7 +128,46 @@ begin |
| 107 |
|
128 |
|
| 108 |
S.Read(ASong, n); |
129 |
S.Read(ASong, n); |
| 109 |
|
130 |
|
| 110 |
// ASong.Patterns.Clear; |
131 |
// Create the patterns |
|
|
132 |
ASong.Patterns := TPatternMap.Create; |
|
|
133 |
// Read the pattern count |
|
|
134 |
S.Read(n, SizeOf(Integer)); |
|
|
135 |
for i:=0 to n - 1 do begin |
|
|
136 |
// Allocate memory for each pattern ... |
|
|
137 |
New(pat); |
|
|
138 |
// and read the pattern content |
|
|
139 |
S.Read(pat^, SizeOf(TPattern)); |
|
|
140 |
// Add the pattern to the list |
|
|
141 |
ASong.Patterns.Add(i, pat); |
|
|
142 |
end; |
|
|
143 |
|
|
|
144 |
// Read the OrderMatrix |
|
|
145 |
for i := 0 to 3 do |
|
|
146 |
begin |
|
|
147 |
// Read length of each OrderMatrix array |
|
|
148 |
S.Read(n, SizeOf(Integer)); |
|
|
149 |
// Allocate memory for it |
|
|
150 |
SetLength(ASong.OrderMatrix[i], n); |
|
|
151 |
// Read content of OrderMatrix array |
|
|
152 |
S.Read(ASong.OrderMatrix[i, 0], n*SizeOf(Integer)); |
|
|
153 |
end; |
|
|
154 |
|
|
|
155 |
for I := Low(TRoutineBank) to High(TRoutineBank) do |
|
|
156 |
ASong.Routines[I] := S.ReadAnsiString; |
|
|
157 |
end; |
|
|
158 |
|
|
|
159 |
procedure ReadSongFromStreamV3(S: TStream; out ASong: TSongV3); |
|
|
160 |
var |
|
|
161 |
i, n: Integer; |
|
|
162 |
pat: PPattern; |
|
|
163 |
begin |
|
|
164 |
// Read the fixed elements first |
|
|
165 |
n := SizeOf(TSongV3) |
|
|
166 |
- SizeOf(TPatternMap) |
|
|
167 |
- SizeOf(TOrderMatrix) |
|
|
168 |
- SizeOf(TRoutineBank); |
|
|
169 |
|
|
|
170 |
S.Read(ASong, n); |
| 111 |
|
171 |
|
| 112 |
// Create the patterns |
172 |
// Create the patterns |
| 113 |
ASong.Patterns := TPatternMap.Create; |
173 |
ASong.Patterns := TPatternMap.Create; |
@@ -143,7 +203,7 @@ var |
| 143 |
pPat: PPattern; |
203 |
pPat: PPattern; |
| 144 |
begin |
204 |
begin |
| 145 |
// Write the fixed record elements first |
205 |
// Write the fixed record elements first |
| 146 |
n := SizeOf(TSongV2) |
206 |
n := SizeOf(TSongV3) |
| 147 |
- SizeOf(TPatternMap) |
207 |
- SizeOf(TPatternMap) |
| 148 |
- SizeOf(TOrderMatrix) |
208 |
- SizeOf(TOrderMatrix) |
| 149 |
- SizeOf(TRoutineBank); |
209 |
- SizeOf(TRoutineBank); |
@@ -173,8 +233,9 @@ end; |
| 173 |
|
233 |
|
| 174 |
procedure ReadSongFromStream(S: TStream; out ASong: TSong); |
234 |
procedure ReadSongFromStream(S: TStream; out ASong: TSong); |
| 175 |
var |
235 |
var |
| 176 |
Version: Integer = 1; |
236 |
Version: Integer; |
| 177 |
SV1: TSongV1; |
237 |
SV1: TSongV1; |
|
|
238 |
SV2: TSongV2; |
| 178 |
begin |
239 |
begin |
| 179 |
S.Read(Version, SizeOf(Integer)); |
240 |
S.Read(Version, SizeOf(Integer)); |
| 180 |
S.Seek(0, soBeginning); |
241 |
S.Seek(0, soBeginning); |
@@ -183,7 +244,11 @@ begin |
| 183 |
ReadSongFromStreamV1(S, SV1); |
244 |
ReadSongFromStreamV1(S, SV1); |
| 184 |
ASong := UpgradeSong(SV1); |
245 |
ASong := UpgradeSong(SV1); |
| 185 |
end; |
246 |
end; |
| 186 |
2: ReadSongFromStreamV2(S, ASong); |
247 |
2: begin |
|
|
248 |
ReadSongFromStreamV2(S, SV2); |
|
|
249 |
ASong := UpgradeSong(SV2); |
|
|
250 |
end; |
|
|
251 |
3: ReadSongFromStreamV3(S, ASong); |
| 187 |
end; |
252 |
end; |
| 188 |
end; |
253 |
end; |
| 189 |
|
254 |
|
@@ -197,8 +262,45 @@ begin |
| 197 |
Artist := ''; |
262 |
Artist := ''; |
| 198 |
Comment := ''; |
263 |
Comment := ''; |
| 199 |
end; |
264 |
end; |
| 200 |
for I := Low(S.Instruments) to High(S.Instruments) do |
265 |
|
| 201 |
with S.Instruments[I] do begin |
266 |
for I := Low(S.Instruments.DutyInstruments) to High(S.Instruments.DutyInstruments) do |
|
|
267 |
with S.Instruments.DutyInstruments[I] do begin |
|
|
268 |
Type_ := itSquare; |
|
|
269 |
Length := 0; |
|
|
270 |
LengthEnabled := False; |
|
|
271 |
InitialVolume := High(TEnvelopeVolume); |
|
|
272 |
VolSweepDirection := Down; |
|
|
273 |
VolSweepAmount := 0; |
|
|
274 |
|
|
|
275 |
SweepTime := 0; |
|
|
276 |
SweepIncDec := Down; |
|
|
277 |
SweepShift := 0; |
|
|
278 |
|
|
|
279 |
Duty := 2; |
|
|
280 |
|
|
|
281 |
OutputLevel := 1; |
|
|
282 |
end; |
|
|
283 |
|
|
|
284 |
for I := Low(S.Instruments.WaveInstruments) to High(S.Instruments.WaveInstruments) do |
|
|
285 |
with S.Instruments.WaveInstruments[I] do begin |
|
|
286 |
Type_ := itWave; |
|
|
287 |
Length := 0; |
|
|
288 |
LengthEnabled := False; |
|
|
289 |
InitialVolume := High(TEnvelopeVolume); |
|
|
290 |
VolSweepDirection := Down; |
|
|
291 |
VolSweepAmount := 0; |
|
|
292 |
|
|
|
293 |
SweepTime := 0; |
|
|
294 |
SweepIncDec := Down; |
|
|
295 |
SweepShift := 0; |
|
|
296 |
|
|
|
297 |
Duty := 2; |
|
|
298 |
|
|
|
299 |
OutputLevel := 1; |
|
|
300 |
end; |
|
|
301 |
|
|
|
302 |
for I := Low(S.Instruments.NoiseInstruments) to High(S.Instruments.NoiseInstruments) do |
|
|
303 |
with S.Instruments.NoiseInstruments[I] do begin |
| 202 |
Type_ := itSquare; |
304 |
Type_ := itSquare; |
| 203 |
Length := 0; |
305 |
Length := 0; |
| 204 |
LengthEnabled := False; |
306 |
LengthEnabled := False; |
@@ -214,6 +316,7 @@ begin |
| 214 |
|
316 |
|
| 215 |
OutputLevel := 1; |
317 |
OutputLevel := 1; |
| 216 |
end; |
318 |
end; |
|
|
319 |
|
| 217 |
for I := Low(S.Waves) to High(S.Waves) do begin |
320 |
for I := Low(S.Waves) to High(S.Waves) do begin |
| 218 |
for J := 0 to 32 do |
321 |
for J := 0 to 32 do |
| 219 |
S.Waves[I][J] := random($F); |
322 |
S.Waves[I][J] := random($F); |