Commit 19e9eac

Nick committed on
Merging three-instrument-banks
commit 19e9eacf7c654f329ecf70a4c2338b36a4f5cfed parent efea754parent 46ba2b3
9 changed files Diffed against the first of 2 parents. +356−131
Modifiedcodegen.pas +21−5
@@ -6,7 +6,8 @@ interface
6 6
7 uses 7 uses
8 Classes, SysUtils, math, Instruments, Song, Utils, 8 Classes, SysUtils, math, Instruments, Song, Utils,
9 HugeDatatypes, Constants, dialogs, strutils, FileUtil, LazFileUtils; 9 HugeDatatypes, Constants, dialogs, strutils, FileUtil, LazFileUtils,
10 lclintf;
10 11
11 type 12 type
12 TExportMode = (emNormal, emPreview, emGBS); 13 TExportMode = (emNormal, emPreview, emGBS);
@@ -56,6 +57,7 @@ var
56 SL, ResultSL: TStringList; 57 SL, ResultSL: TStringList;
57 AsmInstrument: TAsmInstrument; 58 AsmInstrument: TAsmInstrument;
58 I, J: Integer; 59 I, J: Integer;
60 TypePrefix: String;
59 begin 61 begin
60 ResultSL := TStringList.Create; 62 ResultSL := TStringList.Create;
61 63
@@ -68,7 +70,8 @@ begin
68 for J := Low(AsmInstrument) to High(AsmInstrument) do 70 for J := Low(AsmInstrument) to High(AsmInstrument) do
69 SL.Add(IntToStr(AsmInstrument[J])); 71 SL.Add(IntToStr(AsmInstrument[J]));
70 72
71 ResultSL.Add(Format('%s: db %s', ['inst'+IntToStr(I), SL.DelimitedText])); 73 WriteStr(TypePrefix, Instruments[I].Type_);
74 ResultSL.Add(Format('%s%s: db %s', [TypePrefix, 'inst'+IntToStr(I), SL.DelimitedText]));
72 SL.Free; 75 SL.Free;
73 end; 76 end;
74 77
@@ -206,9 +209,19 @@ begin
206 Write(OutFile, RenderOrderTable(Song.OrderMatrix)); 209 Write(OutFile, RenderOrderTable(Song.OrderMatrix));
207 CloseFile(OutFile); 210 CloseFile(OutFile);
208 211
209 AssignFile(OutFile, './hUGEDriver/instrument.htt'); 212 AssignFile(OutFile, './hUGEDriver/duty_instrument.htt');
210 Rewrite(OutFile); 213 Rewrite(OutFile);
211 Write(OutFile, RenderInstruments(Song.Instruments)); 214 Write(OutFile, RenderInstruments(Song.Instruments.Duty));
215 CloseFile(OutFile);
216
217 AssignFile(OutFile, './hUGEDriver/wave_instrument.htt');
218 Rewrite(OutFile);
219 Write(OutFile, RenderInstruments(Song.Instruments.Wave));
220 CloseFile(OutFile);
221
222 AssignFile(OutFile, './hUGEDriver/noise_instrument.htt');
223 Rewrite(OutFile);
224 Write(OutFile, RenderInstruments(Song.Instruments.Noise));
212 CloseFile(OutFile); 225 CloseFile(OutFile);
213 226
214 WriteRoutinesToFile(Song.Routines); 227 WriteRoutinesToFile(Song.Routines);
@@ -295,7 +308,7 @@ begin
295 mtError, 308 mtError,
296 [mbOK], 309 [mbOK],
297 0) 310 0)
298 else 311 else begin
299 MessageDlg( 312 MessageDlg(
300 'Error!', 313 'Error!',
301 'There was an error assembling the song for playback.'+LineEnding+LineEnding+ 314 'There was an error assembling the song for playback.'+LineEnding+LineEnding+
@@ -307,6 +320,9 @@ begin
307 [mbOK], 320 [mbOK],
308 0); 321 0);
309 322
323 {$ifdef PRODUCTION}OpenURL('https://github.com/SuperDisk/UGE/issues');{$endif}
324 end;
325
310 Cleanup: 326 Cleanup:
311 Proc.Free; 327 Proc.Free;
312 OutSL.Free; 328 OutSL.Free;
Modifiedconstants.pas +1−1
@@ -18,7 +18,7 @@ type
18 TRoutineIndex = 0..15; 18 TRoutineIndex = 0..15;
19 19
20 const 20 const
21 UGE_FORMAT_VERSION = 2; 21 UGE_FORMAT_VERSION = 3;
22 22
23 INSTRUMENTS_COUNT = 15; 23 INSTRUMENTS_COUNT = 15;
24 ROUTINES_COUNT = 16; 24 ROUTINES_COUNT = 16;
Modifiedhugedatatypes.pas +67−1
@@ -13,6 +13,12 @@ type
13 TRoutine = string; 13 TRoutine = string;
14 14
15 TInstrumentBank = packed array[1..15] of TInstrument; 15 TInstrumentBank = packed array[1..15] of TInstrument;
16 TInstrumentCollection = packed record
17 case Boolean of
18 False: (Duty, Wave, Noise: TInstrumentBank);
19 True: (All: packed array[1..45] of TInstrument);
20 end;
21
16 TWaveBank = packed array[0..15] of TWave; 22 TWaveBank = packed array[0..15] of TWave;
17 TRoutineBank = packed array[0..15] of TRoutine; 23 TRoutineBank = packed array[0..15] of TRoutine;
18 24
@@ -33,7 +39,17 @@ type
33 TPattern = packed array[0..63] of TCell; 39 TPattern = packed array[0..63] of TCell;
34 PPattern = ^TPattern; 40 PPattern = ^TPattern;
35 41
36 TPatternMap = specialize TFPGMap<Integer, PPattern>; 42 //TPatternMap = specialize TFPGMap<Integer, PPattern>;
43 TPatternMap = class(specialize TFPGMap<Integer, PPattern>)
44 function GetOrCreateNew(Key: Integer): PPattern;
45 function CreateNewPattern(Key: Integer): PPattern;
46 function MaxKey: Integer;
47
48 procedure DeletePattern(Key: Integer);
49
50 destructor Destroy; override;
51 end;
52
37 TOrderMatrix = packed array[0..3] of array of Integer; 53 TOrderMatrix = packed array[0..3] of array of Integer;
38 54
39 TCellPart = ( 55 TCellPart = (
@@ -68,6 +84,8 @@ procedure DecSelectionPos(var SP: TSelectionPos);
68 84
69 implementation 85 implementation
70 86
87 uses Utils;
88
71 operator>(L, R: TSelectionPos): Boolean; 89 operator>(L, R: TSelectionPos): Boolean;
72 begin 90 begin
73 if L.X > R.X then 91 if L.X > R.X then
@@ -121,5 +139,53 @@ begin
121 else Dec(SP.SelectedPart); 139 else Dec(SP.SelectedPart);
122 end; 140 end;
123 141
142 { TPatternMap }
143
144 function TPatternMap.GetOrCreateNew(Key: Integer): PPattern;
145 begin
146 if Self.IndexOf(Key) <> -1 then
147 Result := Self.KeyData[Key]
148 else begin
149 New(Result);
150 BlankPattern(Result);
151 Self.Add(Key, Result);
152 end;
153 end;
154
155 function TPatternMap.CreateNewPattern(Key: Integer): PPattern;
156 begin
157 if IndexOf(Key) <> -1 then Exit(KeyData[Key]);
158
159 New(Result);
160 BlankPattern(Result);
161 Self.Add(Key, Result);
162 end;
163
164 function TPatternMap.MaxKey: Integer;
165 var
166 X: Integer;
167 begin
168 Result := 0;
169 for X := 0 to Self.Count-1 do
170 if Self.Keys[X] > Result then Result := Self.Keys[X];
171 Inc(Result);
172 end;
173
174 procedure TPatternMap.DeletePattern(Key: Integer);
175 begin
176 Dispose(KeyData[Key]);
177 Self.Remove(Key);
178 end;
179
180 destructor TPatternMap.Destroy;
181 var
182 I: Integer;
183 begin
184 for I := 0 to Self.Count-1 do
185 Dispose(Self.Data[I]);
186
187 inherited;
188 end;
189
124 end. 190 end.
125 191
Modifiedinstruments.pas +3−3
@@ -8,10 +8,10 @@ uses
8 Classes, SysUtils; 8 Classes, SysUtils;
9 9
10 type 10 type
11 TInstrumentType = (itSquare, itWave, itNoise); 11 TInstrumentType = (itSquare = 0, itWave = 1, itNoise = 2);
12 TDutyType = 0..3; 12 TDutyType = 0..3;
13 TSweepType = (Up, Down); 13 TSweepType = (Up, Down);
14 TStepWidth = (Fifteen, Seven); 14 TStepWidth = (swFifteen, swSeven);
15 TEnvelopeVolume = 0..15; 15 TEnvelopeVolume = 0..15;
16 TEnvelopeSweepAmount = 0..7; 16 TEnvelopeSweepAmount = 0..7;
17 17
@@ -282,7 +282,7 @@ begin
282 NR43.ShiftClockFrequency := $F - (Frequency shr 7); // Quantize CH4 note 282 NR43.ShiftClockFrequency := $F - (Frequency shr 7); // Quantize CH4 note
283 283
284 NR43.DividingRatio:= Instr.DividingRatio; 284 NR43.DividingRatio:= Instr.DividingRatio;
285 NR43.SevenBitCounter:=Instr.CounterStep = Seven; 285 NR43.SevenBitCounter:=Instr.CounterStep = swSeven;
286 286
287 NR44.Initial:= Initial; 287 NR44.Initial:= Initial;
288 NR44.UseLength:=Instr.LengthEnabled; 288 NR44.UseLength:=Instr.LengthEnabled;
Modifiedmodimport.pas +6−6
@@ -40,7 +40,7 @@ type
40 40
41 { TMODRow } 41 { TMODRow }
42 42
43 TMODRow = bitpacked record 43 TMODRow = record
44 Note: Integer; 44 Note: Integer;
45 Instrument: Integer; 45 Instrument: Integer;
46 Effect: bitpacked record 46 Effect: bitpacked record
@@ -196,13 +196,13 @@ begin
196 // Create the instruments. 196 // Create the instruments.
197 197
198 // First four are just the default instrument with a different duty cycle. 198 // First four are just the default instrument with a different duty cycle.
199 Result.Instruments[1].Duty := 1; 199 Result.Instruments.Duty[1].Duty := 1;
200 Result.Instruments[2].Duty := 2; 200 Result.Instruments.Duty[2].Duty := 2;
201 Result.Instruments[3].Duty := 3; 201 Result.Instruments.Duty[3].Duty := 3;
202 Result.Instruments[4].Duty := 0; 202 Result.Instruments.Duty[4].Duty := 0;
203 203
204 for I := 8 to 15 do 204 for I := 8 to 15 do
205 with Result.Instruments[I] do begin 205 with Result.Instruments.Wave[I] do begin
206 Waveform := I - 8; 206 Waveform := I - 8;
207 Type_ := itWave; 207 Type_ := itWave;
208 end; 208 end;
Modifiedsong.pas +131−9
@@ -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,9 +74,12 @@ 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
81 uses Utils;
82
58 // Thanks to WP on the FreePascal forums for this code! 83 // Thanks to WP on the FreePascal forums for this code!
59 // https://forum.lazarus.freepascal.org/index.php/topic,47892.msg344152.html#msg344152 84 // https://forum.lazarus.freepascal.org/index.php/topic,47892.msg344152.html#msg344152
60 85
@@ -67,8 +92,6 @@ begin
67 n := SizeOf(TSongV1) - SizeOf(TPatternMap) - SizeOf(TOrderMatrix); 92 n := SizeOf(TSongV1) - SizeOf(TPatternMap) - SizeOf(TOrderMatrix);
68 S.Read(ASong, n); 93 S.Read(ASong, n);
69 94
70 // ASong.Patterns.Clear;
71
72 // Create the patterns 95 // Create the patterns
73 ASong.Patterns := TPatternMap.Create; 96 ASong.Patterns := TPatternMap.Create;
74 // Read the pattern count 97 // Read the pattern count
@@ -107,7 +130,46 @@ begin
107 130
108 S.Read(ASong, n); 131 S.Read(ASong, n);
109 132
110 // ASong.Patterns.Clear; 133 // Create the patterns
134 ASong.Patterns := TPatternMap.Create;
135 // Read the pattern count
136 S.Read(n, SizeOf(Integer));
137 for i:=0 to n - 1 do begin
138 // Allocate memory for each pattern ...
139 New(pat);
140 // and read the pattern content
141 S.Read(pat^, SizeOf(TPattern));
142 // Add the pattern to the list
143 ASong.Patterns.Add(i, pat);
144 end;
145
146 // Read the OrderMatrix
147 for i := 0 to 3 do
148 begin
149 // Read length of each OrderMatrix array
150 S.Read(n, SizeOf(Integer));
151 // Allocate memory for it
152 SetLength(ASong.OrderMatrix[i], n);
153 // Read content of OrderMatrix array
154 S.Read(ASong.OrderMatrix[i, 0], n*SizeOf(Integer));
155 end;
156
157 for I := Low(TRoutineBank) to High(TRoutineBank) do
158 ASong.Routines[I] := S.ReadAnsiString;
159 end;
160
161 procedure ReadSongFromStreamV3(S: TStream; out ASong: TSongV3);
162 var
163 i, n: Integer;
164 pat: PPattern;
165 begin
166 // Read the fixed elements first
167 n := SizeOf(TSongV3)
168 - SizeOf(TPatternMap)
169 - SizeOf(TOrderMatrix)
170 - SizeOf(TRoutineBank);
171
172 S.Read(ASong, n);
111 173
112 // Create the patterns 174 // Create the patterns
113 ASong.Patterns := TPatternMap.Create; 175 ASong.Patterns := TPatternMap.Create;
@@ -143,7 +205,7 @@ var
143 pPat: PPattern; 205 pPat: PPattern;
144 begin 206 begin
145 // Write the fixed record elements first 207 // Write the fixed record elements first
146 n := SizeOf(TSongV2) 208 n := SizeOf(TSongV3)
147 - SizeOf(TPatternMap) 209 - SizeOf(TPatternMap)
148 - SizeOf(TOrderMatrix) 210 - SizeOf(TOrderMatrix)
149 - SizeOf(TRoutineBank); 211 - SizeOf(TRoutineBank);
@@ -173,8 +235,9 @@ end;
173 235
174 procedure ReadSongFromStream(S: TStream; out ASong: TSong); 236 procedure ReadSongFromStream(S: TStream; out ASong: TSong);
175 var 237 var
176 Version: Integer = 1; 238 Version: Integer;
177 SV1: TSongV1; 239 SV1: TSongV1;
240 SV2: TSongV2;
178 begin 241 begin
179 S.Read(Version, SizeOf(Integer)); 242 S.Read(Version, SizeOf(Integer));
180 S.Seek(0, soBeginning); 243 S.Seek(0, soBeginning);
@@ -183,7 +246,11 @@ begin
183 ReadSongFromStreamV1(S, SV1); 246 ReadSongFromStreamV1(S, SV1);
184 ASong := UpgradeSong(SV1); 247 ASong := UpgradeSong(SV1);
185 end; 248 end;
186 2: ReadSongFromStreamV2(S, ASong); 249 2: begin
250 ReadSongFromStreamV2(S, SV2);
251 ASong := UpgradeSong(SV2);
252 end;
253 3: ReadSongFromStreamV3(S, ASong);
187 end; 254 end;
188 end; 255 end;
189 256
@@ -197,8 +264,9 @@ begin
197 Artist := ''; 264 Artist := '';
198 Comment := ''; 265 Comment := '';
199 end; 266 end;
200 for I := Low(S.Instruments) to High(S.Instruments) do 267
201 with S.Instruments[I] do begin 268 for I := Low(S.Instruments.Duty) to High(S.Instruments.Duty) do
269 with S.Instruments.Duty[I] do begin
202 Type_ := itSquare; 270 Type_ := itSquare;
203 Length := 0; 271 Length := 0;
204 LengthEnabled := False; 272 LengthEnabled := False;
@@ -214,6 +282,30 @@ begin
214 282
215 OutputLevel := 1; 283 OutputLevel := 1;
216 end; 284 end;
285
286 for I := Low(S.Instruments.Wave) to High(S.Instruments.Wave) do
287 with S.Instruments.Wave[I] do begin
288 Type_ := itWave;
289 Length := 0;
290 LengthEnabled := False;
291 OutputLevel := 1;
292 Waveform := 0;
293 end;
294
295 for I := Low(S.Instruments.Noise) to High(S.Instruments.Noise) do
296 with S.Instruments.Noise[I] do begin
297 Type_ := itNoise;
298 Length := 0;
299 LengthEnabled := False;
300 InitialVolume := High(TEnvelopeVolume);
301 VolSweepDirection := Down;
302 VolSweepAmount := 0;
303
304 ShiftClockFreq := 0;
305 DividingRatio := 0;
306 CounterStep := swFifteen;
307 end;
308
217 for I := Low(S.Waves) to High(S.Waves) do begin 309 for I := Low(S.Waves) to High(S.Waves) do begin
218 for J := 0 to 32 do 310 for J := 0 to 32 do
219 S.Waves[I][J] := random($F); 311 S.Waves[I][J] := random($F);
@@ -253,6 +345,36 @@ begin
253 end; 345 end;
254 346
255 function UpgradeSong(S: TSongV2): TSong; 347 function UpgradeSong(S: TSongV2): TSong;
348 var
349 SV3: TSongV3;
350 I: Integer;
351 begin
352 InitializeSong(SV3);
353 SV3.Patterns.Free; // We already have one in S, so free the one InitializeSong creates.
354
355 SV3.Version:=3;
356 SV3.Name:=S.Name;
357 SV3.Artist:=S.Artist;
358 SV3.Comment:=S.Comment;
359
360 for I := Low(S.Instruments) to High(S.Instruments) do begin
361 case S.Instruments[I].Type_ of
362 itSquare: SV3.Instruments.Duty[I] := S.Instruments[I];
363 itWave: SV3.Instruments.Wave[I] := S.Instruments[I];
364 itNoise: SV3.Instruments.Noise[I] := S.Instruments[I];
365 end;
366 end;
367
368 SV3.Waves:=S.Waves;
369 SV3.TicksPerRow:=S.TicksPerRow;
370 SV3.Patterns:=S.Patterns;
371 SV3.OrderMatrix:=S.OrderMatrix;
372 SV3.Routines:=S.Routines;
373
374 Result := UpgradeSong(SV3);
375 end;
376
377 function UpgradeSong(S: TSongV3): TSong;
256 begin 378 begin
257 Result := S; 379 Result := S;
258 end; 380 end;
Modifiedtracker.lfm +5−5
@@ -1,7 +1,7 @@
1 object frmTracker: TfrmTracker 1 object frmTracker: TfrmTracker
2 Left = 1860 2 Left = 1888
3 Height = 726 3 Height = 726
4 Top = 69 4 Top = 59
5 Width = 1180 5 Width = 1180
6 Caption = 'hUGETracker' 6 Caption = 'hUGETracker'
7 ClientHeight = 706 7 ClientHeight = 706
@@ -60,7 +60,7 @@ object frmTracker: TfrmTracker
60 Height = 480 60 Height = 480
61 Top = 0 61 Top = 0
62 Width = 982 62 Width = 982
63 ActivePage = WavesTabSheet 63 ActivePage = InstrumentTabSheet
64 Align = alClient 64 Align = alClient
65 ParentFont = False 65 ParentFont = False
66 TabIndex = 3 66 TabIndex = 3
@@ -911,8 +911,8 @@ object frmTracker: TfrmTracker
911 end 911 end
912 object WavesTabSheet: TTabSheet 912 object WavesTabSheet: TTabSheet
913 Caption = 'Waves' 913 Caption = 'Waves'
914 ClientHeight = 452 914 ClientHeight = 414
915 ClientWidth = 974 915 ClientWidth = 960
916 ParentFont = False 916 ParentFont = False
917 object WaveEditPaintBox: TPaintBox 917 object WaveEditPaintBox: TPaintBox
918 AnchorSideTop.Control = WaveEditGroupBox 918 AnchorSideTop.Control = WaveEditGroupBox
Modifiedtracker.pas +94−52
@@ -361,6 +361,7 @@ type
361 Song: TSong; 361 Song: TSong;
362 CurrentInstrument: ^TInstrument; 362 CurrentInstrument: ^TInstrument;
363 CurrentWave: ^TWave; 363 CurrentWave: ^TWave;
364 CurrentInstrumentBank: TInstrumentType;
364 365
365 EmulationThread: TThread; 366 EmulationThread: TThread;
366 367
@@ -370,7 +371,12 @@ type
370 LoadingFile: Boolean; 371 LoadingFile: Boolean;
371 SaveSucceeded: Boolean; 372 SaveSucceeded: Boolean;
372 373
373 {PatternsNode, }InstrumentsNode, WavesNode, RoutinesNode: TTreeNode; 374 {PatternsNode, }
375 WaveInstrumentsNode,
376 NoiseInstrumentsNode,
377 DutyInstrumentsNode,
378 WavesNode,
379 RoutinesNode: TTreeNode;
374 380
375 OptionsFile: TIniFile; 381 OptionsFile: TIniFile;
376 382
@@ -379,7 +385,7 @@ type
379 procedure ChangeToSquare; 385 procedure ChangeToSquare;
380 procedure ChangeToWave; 386 procedure ChangeToWave;
381 procedure ChangeToNoise; 387 procedure ChangeToNoise;
382 procedure LoadInstrument(Instr: Integer); 388 procedure LoadInstrument(Bank: TInstrumentType; Instr: Integer);
383 procedure LoadWave(Wave: Integer); 389 procedure LoadWave(Wave: Integer);
384 procedure LoadSong(Filename: String); 390 procedure LoadSong(Filename: String);
385 procedure ReloadPatterns; 391 procedure ReloadPatterns;
@@ -425,7 +431,7 @@ begin
425 ResetEmulationThread; 431 ResetEmulationThread;
426 432
427 LoadingFile := True; // HACK!!!!! 433 LoadingFile := True; // HACK!!!!!
428 LoadInstrument(1); 434 LoadInstrument(itSquare, 1);
429 LoadWave(0); 435 LoadWave(0);
430 436
431 RoutineSynedit.Text := Song.Routines[0]; 437 RoutineSynedit.Text := Song.Routines[0];
@@ -440,11 +446,22 @@ begin
440 RecreateTrackerGrid; 446 RecreateTrackerGrid;
441 CopyOrderMatrixToOrderGrid; 447 CopyOrderMatrixToOrderGrid;
442 448
443 for I := Low(Song.Instruments) to High(song.Instruments) do 449 // PLACEHOLDER
444 InstrumentComboBox.Items[I] := IntToStr(I)+': '+Song.Instruments[I].Name; 450 while InstrumentComboBox.Items.Count > 1 do
445 451 InstrumentComboBox.Items.Delete(1);
446 for I := Low(Song.Instruments) to High(song.Instruments) do 452
447 InstrumentsNode.Items[I-1].Text := IntToStr(I)+': '+Song.Instruments[I].Name; 453 for I := Low(Song.Instruments.Duty) to High(song.Instruments.Duty) do
454 InstrumentComboBox.Items.Add('Square '+IntToStr(I)+': '+Song.Instruments.Duty[ModInst(I)].Name);
455 for I := Low(Song.Instruments.Wave) to High(song.Instruments.Wave) do
456 InstrumentComboBox.Items.Add('Wave '+IntToStr(I)+': '+Song.Instruments.Wave[ModInst(I)].Name);
457 for I := Low(Song.Instruments.Noise) to High(song.Instruments.Noise) do
458 InstrumentComboBox.Items.Add('Noise '+IntToStr(I)+': '+Song.Instruments.Noise[ModInst(I)].Name);
459
460 for I := Low(TInstrumentBank) to High(TInstrumentBank) do begin
461 DutyInstrumentsNode.Items[I-1].Text := IntToStr(I)+': '+Song.Instruments.Duty[I].Name;
462 WaveInstrumentsNode.Items[I-1].Text := IntToStr(I)+': '+Song.Instruments.Wave[I].Name;
463 NoiseInstrumentsNode.Items[I-1].Text := IntToStr(I)+': '+Song.Instruments.Noise[I].Name;
464 end;
448 465
449 LoadingFile := False; // HACK!!!! 466 LoadingFile := False; // HACK!!!!
450 467
@@ -566,11 +583,11 @@ begin
566 // TODO: Find some way to synchronize with the playback thread, so it doesn't 583 // TODO: Find some way to synchronize with the playback thread, so it doesn't
567 // fail sometimes. 584 // fail sometimes.
568 585
569 with Song.Instruments[Instr] do 586 with Song.Instruments.All[Instr] do //PLACEHOLDER
570 begin 587 begin
571 case Type_ of 588 case Type_ of
572 itSquare: begin 589 itSquare: begin
573 Regs := SquareInstrumentToRegisters(Freq, True, Song.Instruments[Instr]); 590 Regs := SquareInstrumentToRegisters(Freq, True, Song.Instruments.All[Instr]);
574 Spokeb(NR10, Regs.NR10); 591 Spokeb(NR10, Regs.NR10);
575 Spokeb(NR11, Regs.NR11); 592 Spokeb(NR11, Regs.NR11);
576 Spokeb(NR12, Regs.NR12); 593 Spokeb(NR12, Regs.NR12);
@@ -583,7 +600,7 @@ begin
583 for I := Low(NewWaveform) to High(NewWaveform) do 600 for I := Low(NewWaveform) to High(NewWaveform) do
584 Spokeb(AUD3_WAVE_RAM+I, NewWaveform[I]); 601 Spokeb(AUD3_WAVE_RAM+I, NewWaveform[I]);
585 602
586 Regs := WaveInstrumentToRegisters(Freq, True, Song.Instruments[Instr]); 603 Regs := WaveInstrumentToRegisters(Freq, True, Song.Instruments.All[Instr]);
587 604
588 Spokeb(NR30, Regs.NR30); 605 Spokeb(NR30, Regs.NR30);
589 Spokeb(NR31, Regs.NR31); 606 Spokeb(NR31, Regs.NR31);
@@ -592,7 +609,7 @@ begin
592 Spokeb(NR34, Regs.NR34) 609 Spokeb(NR34, Regs.NR34)
593 end; 610 end;
594 itNoise: begin 611 itNoise: begin
595 Regs := NoiseInstrumentToRegisters(Freq, True, Song.Instruments[Instr]); 612 Regs := NoiseInstrumentToRegisters(Freq, True, Song.Instruments.All[Instr]);
596 Spokeb(NR41, Regs.NR41); 613 Spokeb(NR41, Regs.NR41);
597 Spokeb(NR42, Regs.NR42); 614 Spokeb(NR42, Regs.NR42);
598 Spokeb(NR43, Regs.NR43); 615 Spokeb(NR43, Regs.NR43);
@@ -605,7 +622,7 @@ end;
605 procedure TfrmTracker.PreviewC5; 622 procedure TfrmTracker.PreviewC5;
606 begin 623 begin
607 if not LoadingFile then begin 624 if not LoadingFile then begin
608 PreviewInstrument(NotesToFreqs.Data[C_5], InstrumentNumberSpinner.Value); 625 PreviewInstrument(NotesToFreqs.Data[C_5], UnmodInst(CurrentInstrumentBank, InstrumentNumberSpinner.Value));
609 NoteHaltTimer.Enabled := False; 626 NoteHaltTimer.Enabled := False;
610 NoteHaltTimer.Enabled := True 627 NoteHaltTimer.Enabled := True
611 end; 628 end;
@@ -808,13 +825,19 @@ begin
808 TrackerGrid.PopupMenu := TrackerGridPopup; 825 TrackerGrid.PopupMenu := TrackerGridPopup;
809 end; 826 end;
810 827
811 procedure TfrmTracker.LoadInstrument(Instr: Integer); 828 procedure TfrmTracker.LoadInstrument(Bank: TInstrumentType; Instr: Integer);
812 var 829 var
813 CI: ^TInstrument; 830 CI: ^TInstrument;
814 begin 831 begin
815 CurrentInstrument := @Song.Instruments[Instr]; 832 CurrentInstrumentBank := Bank;
833 case Bank of
834 itSquare: CurrentInstrument := @Song.Instruments.Duty[Instr];
835 itWave: CurrentInstrument := @Song.Instruments.Wave[Instr];
836 itNoise: CurrentInstrument := @Song.Instruments.Noise[Instr];
837 end;
816 CI := CurrentInstrument; 838 CI := CurrentInstrument;
817 839
840 InstrumentTypeComboBox.ItemIndex := Integer(CurrentInstrumentBank);
818 InstrumentNumberSpinner.Value := Instr; 841 InstrumentNumberSpinner.Value := Instr;
819 InstrumentNameEdit.Text := CI^.Name; 842 InstrumentNameEdit.Text := CI^.Name;
820 LengthEnabledCheckbox.Checked := CI^.LengthEnabled; 843 LengthEnabledCheckbox.Checked := CI^.LengthEnabled;
@@ -861,7 +884,7 @@ begin
861 itNoise: begin 884 itNoise: begin
862 ShiftClockTrackbar.Position := CI^.ShiftClockFreq; 885 ShiftClockTrackbar.Position := CI^.ShiftClockFreq;
863 DivRatioTrackbar.Position := CI^.DividingRatio; 886 DivRatioTrackbar.Position := CI^.DividingRatio;
864 SevenBitCounterCheckbox.Checked := CI^.CounterStep = Seven; 887 SevenBitCounterCheckbox.Checked := CI^.CounterStep = swSeven;
865 end; 888 end;
866 end; 889 end;
867 890
@@ -932,9 +955,9 @@ end;
932 procedure TfrmTracker.SevenBitCounterCheckboxChange(Sender: TObject); 955 procedure TfrmTracker.SevenBitCounterCheckboxChange(Sender: TObject);
933 begin 956 begin
934 if SevenBitCounterCheckbox.Checked then 957 if SevenBitCounterCheckbox.Checked then
935 CurrentInstrument^.CounterStep := Seven 958 CurrentInstrument^.CounterStep := swSeven
936 else 959 else
937 CurrentInstrument^.CounterStep := Fifteen; 960 CurrentInstrument^.CounterStep := swFifteen;
938 end; 961 end;
939 962
940 procedure TfrmTracker.SongEditChange(Sender: TObject); 963 procedure TfrmTracker.SongEditChange(Sender: TObject);
@@ -973,20 +996,12 @@ end;
973 996
974 procedure TfrmTracker.InstrumentTypeComboboxChange(Sender: TObject); 997 procedure TfrmTracker.InstrumentTypeComboboxChange(Sender: TObject);
975 begin 998 begin
976 case InstrumentTypeComboBox.Text of 999 case InstrumentTypeCombobox.ItemIndex of
977 'Square': begin 1000 0: CurrentInstrumentBank := itSquare;
978 CurrentInstrument^.Type_ := itSquare; 1001 1: CurrentInstrumentBank := itWave;
979 ChangeToSquare 1002 2: CurrentInstrumentBank := itNoise;
980 end;
981 'Wave': begin
982 CurrentInstrument^.Type_ := itWave;
983 ChangeToWave
984 end;
985 'Noise': begin
986 CurrentInstrument^.Type_ := itNoise;
987 ChangeToNoise
988 end;
989 end; 1003 end;
1004 LoadInstrument(CurrentInstrumentBank, InstrumentNumberSpinner.Value);
990 end; 1005 end;
991 1006
992 procedure TfrmTracker.RandomizeNoiseButtonClick(Sender: TObject); 1007 procedure TfrmTracker.RandomizeNoiseButtonClick(Sender: TObject);
@@ -998,7 +1013,7 @@ begin
998 LengthTrackbar.Position := Random(Round(LengthTrackbar.Max)); 1013 LengthTrackbar.Position := Random(Round(LengthTrackbar.Max));
999 1014
1000 PreviewInstrument(NotesToFreqs.KeyData[RandomRange(LOWEST_NOTE, HIGHEST_NOTE)], 1015 PreviewInstrument(NotesToFreqs.KeyData[RandomRange(LOWEST_NOTE, HIGHEST_NOTE)],
1001 InstrumentNumberSpinner.Value); 1016 UnmodInst(CurrentInstrumentBank, InstrumentNumberSpinner.Value));
1002 end; 1017 end;
1003 1018
1004 procedure TfrmTracker.FormCreate(Sender: TObject); 1019 procedure TfrmTracker.FormCreate(Sender: TObject);
@@ -1040,31 +1055,33 @@ begin
1040 // Initialize ticks per row 1055 // Initialize ticks per row
1041 Song.TicksPerRow := TicksPerRowSpinEdit.Value; 1056 Song.TicksPerRow := TicksPerRowSpinEdit.Value;
1042 1057
1043 LoadInstrument(1); 1058 LoadInstrument(itSquare, 1);
1044 LoadWave(0); 1059 LoadWave(0);
1045 1060
1046 // Fetch the tree items 1061 // Fetch the tree items
1047 with TreeView1 do begin 1062 with TreeView1 do begin
1048 //PatternsNode := Items[0]; 1063 //PatternsNode := Items[0];
1049 InstrumentsNode := Items[0]; 1064 DutyInstrumentsNode := Items[0].Items[0];
1050 WavesNode := Items[1]; 1065 WaveInstrumentsNode := Items[0].Items[1];
1051 RoutinesNode := Items[2]; 1066 NoiseInstrumentsNode := Items[0].Items[2];
1067
1068 // TODO: Find out how to actually do this... WTF?
1069 WavesNode := Items[4];
1070 RoutinesNode := Items[5];
1052 end; 1071 end;
1053 1072
1054 for PUI := 1 to 15 do 1073 for PUI := 1 to 15 do begin
1055 TreeView1.Items.AddChild(InstrumentsNode, IntToStr(PUI)+':').Data := {%H-}Pointer(PUI); 1074 TreeView1.Items.AddChild(DutyInstrumentsNode, IntToStr(PUI)+':').Data := {%H-}Pointer(PUI);
1075 TreeView1.Items.AddChild(WaveInstrumentsNode, IntToStr(PUI)+':').Data := {%H-}Pointer(PUI);
1076 TreeView1.Items.AddChild(NoiseInstrumentsNode, IntToStr(PUI)+':').Data := {%H-}Pointer(PUI);
1077 end;
1056 1078
1057 for PUI := 0 to 15 do begin 1079 for PUI := 0 to 15 do begin
1058 TreeView1.Items.AddChild(WavesNode, 'Wave '+IntToStr(PUI)).Data := {%H-}Pointer(PUI); 1080 TreeView1.Items.AddChild(WavesNode, 'Wave '+IntToStr(PUI)).Data := {%H-}Pointer(PUI);
1059 TreeView1.Items.AddChild(RoutinesNode, 'Routine '+IntToStr(PUI)).Data := {%H-}Pointer(PUI); 1081 TreeView1.Items.AddChild(RoutinesNode, 'Routine '+IntToStr(PUI)).Data := {%H-}Pointer(PUI);
1060 end; 1082 end;
1061 1083
1062 // Initialize order table 1084 // Initialize order table (InitializeSong creates the default order table)
1063 {for I := 0 to 3 do begin
1064 OrderEditStringGrid.Cells[I+1, 1] := IntToStr(I);
1065 TrackerGrid.LoadPattern(I, I);
1066 end;}
1067 //CopyOrderGridToOrderMatrix;
1068 CopyOrderMatrixToOrderGrid; 1085 CopyOrderMatrixToOrderGrid;
1069 1086
1070 // Manually resize the fixed column in the order editor 1087 // Manually resize the fixed column in the order editor
@@ -1306,7 +1323,7 @@ end;
1306 1323
1307 procedure TfrmTracker.InstrumentComboBoxChange(Sender: TObject); 1324 procedure TfrmTracker.InstrumentComboBoxChange(Sender: TObject);
1308 begin 1325 begin
1309 TrackerGrid.SelectedInstrument := InstrumentComboBox.ItemIndex; 1326 TrackerGrid.SelectedInstrument := ModInst(InstrumentComboBox.ItemIndex);
1310 end; 1327 end;
1311 1328
1312 procedure TfrmTracker.EditDelete1Execute(Sender: TObject); 1329 procedure TfrmTracker.EditDelete1Execute(Sender: TObject);
@@ -1456,17 +1473,30 @@ begin
1456 end; 1473 end;
1457 1474
1458 procedure TfrmTracker.InstrumentNameEditChange(Sender: TObject); 1475 procedure TfrmTracker.InstrumentNameEditChange(Sender: TObject);
1476 var
1477 S: String;
1459 begin 1478 begin
1479 S := IntToStr(InstrumentNumberSpinner.Value) + ': ' + InstrumentNameEdit.Text;
1460 CurrentInstrument^.Name := InstrumentNameEdit.Text; 1480 CurrentInstrument^.Name := InstrumentNameEdit.Text;
1461 InstrumentComboBox.Items[InstrumentNumberSpinner.Value] := 1481 case CurrentInstrumentBank of
1462 IntToStr(InstrumentNumberSpinner.Value) + ': ' + InstrumentNameEdit.Text; 1482 itSquare: begin
1463 InstrumentsNode.Items[InstrumentNumberSpinner.Value-1].Text := 1483 InstrumentComboBox.Items[(0*15) + InstrumentNumberSpinner.Value] := 'Square '+S;
1464 InstrumentComboBox.Items[InstrumentNumberSpinner.Value]; 1484 DutyInstrumentsNode.Items[InstrumentNumberSpinner.Value-1].Text := S;
1485 end;
1486 itWave: begin
1487 InstrumentComboBox.Items[(1*15) + InstrumentNumberSpinner.Value] := 'Wave '+S;
1488 WaveInstrumentsNode.Items[InstrumentNumberSpinner.Value-1].Text := S;
1489 end;
1490 itNoise: begin
1491 InstrumentComboBox.Items[(2*15) + InstrumentNumberSpinner.Value] := 'Noise '+S;
1492 NoiseInstrumentsNode.Items[InstrumentNumberSpinner.Value-1].Text := S;
1493 end;
1494 end;
1465 end; 1495 end;
1466 1496
1467 procedure TfrmTracker.InstrumentNumberSpinnerChange(Sender: TObject); 1497 procedure TfrmTracker.InstrumentNumberSpinnerChange(Sender: TObject);
1468 begin 1498 begin
1469 LoadInstrument(InstrumentNumberSpinner.Value); 1499 LoadInstrument(CurrentInstrumentBank, InstrumentNumberSpinner.Value);
1470 end; 1500 end;
1471 1501
1472 procedure TfrmTracker.LengthSpinnerChange(Sender: TObject); 1502 procedure TfrmTracker.LengthSpinnerChange(Sender: TObject);
@@ -1971,8 +2001,18 @@ end;
1971 2001
1972 procedure TfrmTracker.TreeView1DblClick(Sender: TObject); 2002 procedure TfrmTracker.TreeView1DblClick(Sender: TObject);
1973 begin 2003 begin
1974 if TreeView1.Selected.Parent = InstrumentsNode then begin 2004 if TreeView1.Selected.Parent = DutyInstrumentsNode then begin
1975 InstrumentNumberSpinner.Value := {%H-}PtrUInt(TreeView1.Selected.Data); 2005 LoadInstrument(itSquare, {%H-}PtrUInt(TreeView1.Selected.Data));
2006 PageControl1.ActivePage := InstrumentTabSheet;
2007 end;
2008
2009 if TreeView1.Selected.Parent = WaveInstrumentsNode then begin
2010 LoadInstrument(itWave, {%H-}PtrUInt(TreeView1.Selected.Data));
2011 PageControl1.ActivePage := InstrumentTabSheet;
2012 end;
2013
2014 if TreeView1.Selected.Parent = NoiseInstrumentsNode then begin
2015 LoadInstrument(itNoise, {%H-}PtrUInt(TreeView1.Selected.Data));
1976 PageControl1.ActivePage := InstrumentTabSheet; 2016 PageControl1.ActivePage := InstrumentTabSheet;
1977 end; 2017 end;
1978 2018
@@ -2003,6 +2043,8 @@ procedure TfrmTracker.WaveEditPaintBoxMouseMove(Sender: TObject;
2003 var 2043 var
2004 Idx: Integer; 2044 Idx: Integer;
2005 begin 2045 begin
2046 X := EnsureRange(X, 0, WaveEditPaintBox.Width);
2047 Y := EnsureRange(Y, 0, WaveEditPaintBox.Height);
2006 if DrawingWave then begin 2048 if DrawingWave then begin
2007 Idx := EnsureRange(Trunc((X / WaveEditPaintBox.Width)*High(TWave)), Low(TWave), High(TWave)); 2049 Idx := EnsureRange(Trunc((X / WaveEditPaintBox.Width)*High(TWave)), Low(TWave), High(TWave));
2008 CurrentWave^[Idx] := EnsureRange(Trunc((Y / WaveEditPaintBox.Height)*$F), 0, $F); 2050 CurrentWave^[Idx] := EnsureRange(Trunc((Y / WaveEditPaintBox.Height)*$F), 0, $F);
Modifiedutils.pas +28−49
@@ -5,7 +5,7 @@ unit Utils;
5 interface 5 interface
6 6
7 uses 7 uses
8 Classes, SysUtils, Song, Waves, HugeDatatypes, Constants, gHashSet; 8 Classes, SysUtils, Song, Waves, HugeDatatypes, Constants, gHashSet, fgl, instruments;
9 9
10 type 10 type
11 11
@@ -17,16 +17,6 @@ type
17 17
18 TUsedMap = specialize THashSet<Integer, TIntegerHash>; 18 TUsedMap = specialize THashSet<Integer, TIntegerHash>;
19 19
20 { TOrderMapHelper }
21
22 TOrderMapHelper = class helper for TPatternMap
23 function GetOrCreateNew(Key: Integer): PPattern;
24 function CreateNewPattern(Key: Integer): PPattern;
25 function MaxKey: Integer;
26
27 procedure DeletePattern(Key: Integer);
28 end;
29
30 { TSongUsageReport } 20 { TSongUsageReport }
31 21
32 TSongUsageReport = record 22 TSongUsageReport = record
@@ -46,6 +36,10 @@ procedure BlankCell(var Cell: TCell);
46 function EffectCodeToStr(Code: Integer; Params: TEffectParams): String; 36 function EffectCodeToStr(Code: Integer; Params: TEffectParams): String;
47 function EffectToExplanation(Code: Integer; Params: TEffectParams): String; 37 function EffectToExplanation(Code: Integer; Params: TEffectParams): String;
48 38
39 function ModInst(Inst: Integer): Integer;
40 function UnmodInst(Bank: TInstrumentType; Inst: Integer): Integer;
41 function InstBankName(Bank: TInstrumentType): String;
42
49 function GetUsageReport(Song: TSong): TSongUsageReport; 43 function GetUsageReport(Song: TSong): TSongUsageReport;
50 procedure FreeUsageReport(Report: TSongUsageReport); 44 procedure FreeUsageReport(Report: TSongUsageReport);
51 45
@@ -58,44 +52,6 @@ begin
58 Result := I mod N; 52 Result := I mod N;
59 end; 53 end;
60 54
61 { TOrderMapHelper }
62
63 function TOrderMapHelper.GetOrCreateNew(Key: Integer): PPattern;
64 begin
65 if Self.IndexOf(Key) <> -1 then
66 Result := Self.KeyData[Key]
67 else begin
68 New(Result);
69 BlankPattern(Result);
70 Self.Add(Key, Result);
71 end;
72 end;
73
74 function TOrderMapHelper.CreateNewPattern(Key: Integer): PPattern;
75 begin
76 if IndexOf(Key) <> -1 then Exit(KeyData[Key]);
77
78 New(Result);
79 BlankPattern(Result);
80 Self.Add(Key, Result);
81 end;
82
83 function TOrderMapHelper.MaxKey: Integer;
84 var
85 X: Integer;
86 begin
87 Result := 0;
88 for X := 0 to Self.Count-1 do
89 if Self.Keys[X] > Result then Result := Self.Keys[X];
90 Inc(Result);
91 end;
92
93 procedure TOrderMapHelper.DeletePattern(Key: Integer);
94 begin
95 Dispose(KeyData[Key]);
96 Self.Remove(Key);
97 end;
98
99 function Lerp(v0, v1, t: Double): Double; 55 function Lerp(v0, v1, t: Double): Double;
100 begin 56 begin
101 Result := ((1 - t) * v0) + (t * v1); 57 Result := ((1 - t) * v0) + (t * v1);
@@ -156,6 +112,29 @@ begin
156 end; 112 end;
157 end; 113 end;
158 114
115 function ModInst(Inst: Integer): Integer;
116 begin
117 Result := ((Inst-1) mod 15)+1;
118 end;
119
120 function UnmodInst(Bank: TInstrumentType; Inst: Integer): Integer;
121 begin
122 case Bank of
123 itSquare: Result := Inst+(15*0);
124 itWave: Result := Inst+(15*1);
125 itNoise: Result := Inst+(15*2);
126 end;
127 end;
128
129 function InstBankName(Bank: TInstrumentType): String;
130 begin
131 case Bank of
132 itSquare: Result := 'Square';
133 itWave: Result := 'Wave';
134 itNoise: Result := 'Noise';
135 end;
136 end;
137
159 function GetUsageReport(Song: TSong): TSongUsageReport; 138 function GetUsageReport(Song: TSong): TSongUsageReport;
160 var 139 var
161 I, J, X, Y: Integer; 140 I, J, X, Y: Integer;