Commit abec48b

Nick committed on
WIP track editing with keyborad
commit abec48b4e135cce70d078159bb49870c7501473a parent a80040c
5 changed files +192−63
ModifiedREADME.md +3−6
@@ -1,8 +1,5 @@
1 # UGE 1 # hUGETracker
2 Unnamed Gameboy Emulator
3 2
4 I'm dumping the source code of an old Gameboy emulator from 2000. It was written by Christian Hackbart and formerly located at http://www.tu-ilmenau.de/~hackbart but the site seems to have gone down long ago. So to preserve it, here it is now. My interest was piqued because despite the glut of Gameboy emulators out there for almost every language, Pascal doesn't seem to have a contender in the ring... except this one. 3 This is the repository for hUGETracker, the music editing suite for the Gameboy.
5 4
6 I ran the original Delphi source through Lazarus' converter tool and made a few tweaks so it would compile with Lazarus 32-bit. DirectDraw support is busted, so DIB is on by deafult. The emulator runs insanely fast, and actually crashes if you leave the sound option on, since (I think) the sound buffer fills up and it starts locking up the whole program. 5 This is all still WIP so check back later.
7
8 My eventual goal is to fix the speed and sound issues, then frankenstein it into the sound engine for a Gameboy music tracker.
Modifiedclipboardutils.pas +1−1
@@ -28,7 +28,7 @@ begin
28 Result.Note := NoteToCodeMap.KeyData[String(MPCell.Note)]; 28 Result.Note := NoteToCodeMap.KeyData[String(MPCell.Note)];
29 Result.Instrument := StrToInt(String(MPCell.Instrument)); 29 Result.Instrument := StrToInt(String(MPCell.Instrument));
30 Result.EffectCode := StrToInt(String('0x'+MPCell.EffectCode)); 30 Result.EffectCode := StrToInt(String('0x'+MPCell.EffectCode));
31 Result.EffectParams := StrToInt(String('0x'+MPCell.EffectParams)); 31 Result.EffectParams.Value := StrToInt(String('0x'+MPCell.EffectParams));
32 end; 32 end;
33 33
34 function GetPastedCells: TSelection; 34 function GetPastedCells: TSelection;
Modifiedconstants.pas +36−34
@@ -193,6 +193,8 @@ function NoteCodeToString(NoteCode: Integer): String;
193 193
194 implementation 194 implementation
195 195
196 uses LCLType;
197
196 function NoteCodeToString(NoteCode: Integer): String; 198 function NoteCodeToString(NoteCode: Integer): String;
197 begin 199 begin
198 if not NoteMap.TryGetData(NoteCode, Result) then 200 if not NoteMap.TryGetData(NoteCode, Result) then
@@ -205,40 +207,40 @@ begin
205 NotesToFreqs := TNoteToFreqMap.Create; 207 NotesToFreqs := TNoteToFreqMap.Create;
206 NoteToCodeMap := TNoteToCodeMap.Create; 208 NoteToCodeMap := TNoteToCodeMap.Create;
207 209
208 Keybindings.Add(Ord('Q'), C_4); 210 Keybindings.Add(VK_Q, C_4);
209 Keybindings.Add(Ord('W'), CS4); 211 Keybindings.Add(VK_W, CS4);
210 Keybindings.Add(Ord('E'), D_4); 212 Keybindings.Add(VK_E, D_4);
211 Keybindings.Add(Ord('R'), DS4); 213 Keybindings.Add(VK_R, DS4);
212 Keybindings.Add(Ord('T'), E_4); 214 Keybindings.Add(VK_T, E_4);
213 Keybindings.Add(Ord('Y'), F_4); 215 Keybindings.Add(VK_Y, F_4);
214 Keybindings.Add(Ord('U'), FS4); 216 Keybindings.Add(VK_U, FS4);
215 Keybindings.Add(Ord('I'), G_4); 217 Keybindings.Add(VK_I, G_4);
216 Keybindings.Add(Ord('O'), GS4); 218 Keybindings.Add(VK_O, GS4);
217 Keybindings.Add(Ord('P'), A_4); 219 Keybindings.Add(VK_P, A_4);
218 Keybindings.Add(Ord('['), AS4); 220 Keybindings.Add(VK_OEM_4, AS4);
219 Keybindings.Add(Ord(']'), B_4); 221 Keybindings.Add(VK_OEM_6, B_4);
220 Keybindings.Add(Ord('\'), B_5); 222 Keybindings.Add(VK_OEM_5, B_5);
221 Keybindings.Add(Ord('A'), C_5); 223 Keybindings.Add(VK_A, C_5);
222 Keybindings.Add(Ord('S'), CS5); 224 Keybindings.Add(VK_S, CS5);
223 Keybindings.Add(Ord('D'), D_5); 225 Keybindings.Add(VK_D, D_5);
224 Keybindings.Add(Ord('F'), DS5); 226 Keybindings.Add(VK_F, DS5);
225 Keybindings.Add(Ord('G'), E_5); 227 Keybindings.Add(VK_G, E_5);
226 Keybindings.Add(Ord('H'), F_5); 228 Keybindings.Add(VK_H, F_5);
227 Keybindings.Add(Ord('J'), FS5); 229 Keybindings.Add(VK_J, FS5);
228 Keybindings.Add(Ord('K'), G_5); 230 Keybindings.Add(VK_K, G_5);
229 Keybindings.Add(Ord('L'), GS5); 231 Keybindings.Add(VK_L, GS5);
230 Keybindings.Add(Ord(';'), A_5); 232 Keybindings.Add(VK_OEM_1, A_5);
231 Keybindings.Add(Ord(''''), AS5); 233 Keybindings.Add(VK_OEM_7, AS5);
232 Keybindings.Add(Ord('Z'), C_6); 234 Keybindings.Add(VK_Z, C_6);
233 Keybindings.Add(Ord('X'), CS6); 235 Keybindings.Add(VK_X, CS6);
234 Keybindings.Add(Ord('C'), D_6); 236 Keybindings.Add(VK_C, D_6);
235 Keybindings.Add(Ord('V'), DS6); 237 Keybindings.Add(VK_V, DS6);
236 Keybindings.Add(Ord('B'), E_6); 238 Keybindings.Add(VK_B, E_6);
237 Keybindings.Add(Ord('N'), F_6); 239 Keybindings.Add(VK_N, F_6);
238 Keybindings.Add(Ord('M'), FS6); 240 Keybindings.Add(VK_M, FS6);
239 Keybindings.Add(Ord(','), G_6); 241 Keybindings.Add(VK_OEM_COMMA, G_6);
240 Keybindings.Add(Ord('.'), GS6); 242 Keybindings.Add(VK_OEM_PERIOD, GS6);
241 Keybindings.Add(Ord('/'), A_6); 243 Keybindings.Add(VK_OEM_2, A_6);
242 244
243 NoteToCodeMap.add('C-3', C_3); 245 NoteToCodeMap.add('C-3', C_3);
244 NoteToCodeMap.add('C#3', Cs3); 246 NoteToCodeMap.add('C#3', Cs3);
Modifiedhugedatatypes.pas +7−1
@@ -8,11 +8,17 @@ uses
8 Classes, SysUtils; 8 Classes, SysUtils;
9 9
10 type 10 type
11 TEffectParams = packed record
12 case Boolean of
13 True: (Param1, Param2: Byte);
14 False: (Value: Word);
15 end;
16
11 TCell = record 17 TCell = record
12 Note: Integer; 18 Note: Integer;
13 Instrument: Integer; 19 Instrument: Integer;
14 EffectCode: Integer; 20 EffectCode: Integer;
15 EffectParams: Integer; 21 EffectParams: TEffectParams;
16 end; 22 end;
17 23
18 TSelection = array of array of TCell; 24 TSelection = array of array of TCell;
Modifiedtrackergrid.pas +145−21
@@ -24,7 +24,13 @@ const
24 NumRows = 64; 24 NumRows = 64;
25 25
26 type 26 type
27 TCellPart = (cpNote = 0, cpInstrument = 1, cpVolume = 2, cpEffect = 3); 27 TCellPart = (
28 cpNote = 0,
29 cpInstrument = 1,
30 cpVolume = 2,
31 cpEffectCode = 3,
32 cpEffectParams = 4
33 );
28 34
29 TSelectionPos = record 35 TSelectionPos = record
30 X, Y: Integer; 36 X, Y: Integer;
@@ -50,6 +56,12 @@ type
50 procedure RenderSelectedArea; 56 procedure RenderSelectedArea;
51 procedure ClampCursors; 57 procedure ClampCursors;
52 58
59 procedure InputNote(Key: Word);
60 procedure InputInstrument(Key: Word);
61 procedure InputVolume(Key: Word);
62 procedure InputEffectCode(Key: Word);
63 procedure InputEffectParams(Key: Word);
64
53 private 65 private
54 Cells: array[0..3, 0..63] of TCell; 66 Cells: array[0..3, 0..63] of TCell;
55 67
@@ -61,6 +73,8 @@ type
61 MouseButtonDown: Boolean; 73 MouseButtonDown: Boolean;
62 Selecting: Boolean; 74 Selecting: Boolean;
63 75
76 DigitInputting: Boolean;
77
64 procedure RenderRow(Row: Integer); 78 procedure RenderRow(Row: Integer);
65 procedure RenderCell(Cell: TCell); 79 procedure RenderCell(Cell: TCell);
66 80
@@ -68,7 +82,8 @@ type
68 function SelectionsToRect(S1, S2: TSelectionPos): TRect; 82 function SelectionsToRect(S1, S2: TSelectionPos): TRect;
69 function SelectionToRect(Selection: TSelectionPos): TRect; 83 function SelectionToRect(Selection: TSelectionPos): TRect;
70 function MousePosToSelection(X, Y: Integer): TSelectionPos; 84 function MousePosToSelection(X, Y: Integer): TSelectionPos;
71 85 function KeycodeToHexNumber(Key: Word; out Num: Byte): Boolean; overload;
86 function KeycodeToHexNumber(Key: Word; out Num: Integer): Boolean; overload;
72 public 87 public
73 Cursor, Other: TSelectionPos; 88 Cursor, Other: TSelectionPos;
74 end; 89 end;
@@ -135,6 +150,7 @@ begin
135 end; 150 end;
136 end; 151 end;
137 152
153 // Draw borders between columns
138 Canvas.Pen.Color := TColor($ABB7BC); 154 Canvas.Pen.Color := TColor($ABB7BC);
139 Canvas.Pen.Width := 2; 155 Canvas.Pen.Width := 2;
140 R := TRect.Create(0, 0, ColumnWidth+1, Height); 156 R := TRect.Create(0, 0, ColumnWidth+1, Height);
@@ -156,16 +172,17 @@ procedure TTrackerGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
156 begin 172 begin
157 inherited MouseDown(Button, Shift, X, Y); 173 inherited MouseDown(Button, Shift, X, Y);
158 174
159 MouseButtonDown := True; 175 if Button = mbLeft then
176 MouseButtonDown := True;
160 Selecting := False; 177 Selecting := False;
178 DigitInputting := False;
161 179
162 if not (csDesigning in ComponentState) and CanFocus then 180 if not (csDesigning in ComponentState) and CanFocus then
163 SetFocus; 181 SetFocus;
164 182
165 Cursor := MousePosToSelection(X, Y); 183 Cursor := MousePosToSelection(X, Y);
166
167 ClampCursors;
168 Other := Cursor; 184 Other := Cursor;
185 ClampCursors;
169 186
170 Invalidate; 187 Invalidate;
171 end; 188 end;
@@ -190,22 +207,29 @@ begin
190 inherited MouseUp(Button, Shift, X, Y); 207 inherited MouseUp(Button, Shift, X, Y);
191 208
192 MouseButtonDown := False; 209 MouseButtonDown := False;
210 DigitInputting := False;
193 end; 211 end;
194 212
195 procedure TTrackerGrid.KeyDown(var Key: Word; Shift: TShiftState); 213 procedure TTrackerGrid.KeyDown(var Key: Word; Shift: TShiftState);
196 begin 214 begin
197 inherited KeyDown(Key, Shift); 215 inherited KeyDown(Key, Shift);
198 216
199 Writeln(Cursor.SelectedPart, ' ', High(TCellPart), ' ', Low(TCellPart));
200 case Key of 217 case Key of
201 VK_UP: Cursor.Y -= 1; 218 VK_UP: begin
202 VK_DOWN: Cursor.Y += 1; 219 Cursor.Y -= 1;
220 DigitInputting := False;
221 end;
222 VK_DOWN: begin
223 Cursor.Y += 1;
224 DigitInputting := False;
225 end;
203 VK_LEFT: begin 226 VK_LEFT: begin
204 if ssCtrl in Shift then 227 if ssCtrl in Shift then
205 Cursor.X -= 1 228 Cursor.X -= 1
206 else if Cursor.SelectedPart > Low(TCellPart) then begin 229 else if Cursor.SelectedPart > Low(TCellPart) then begin
207 Cursor.SelectedPart := Pred(Cursor.SelectedPart); 230 Cursor.SelectedPart := Pred(Cursor.SelectedPart);
208 end; 231 end;
232 DigitInputting := False;
209 end; 233 end;
210 VK_RIGHT: begin 234 VK_RIGHT: begin
211 if ssCtrl in Shift then 235 if ssCtrl in Shift then
@@ -213,13 +237,15 @@ begin
213 else if Cursor.SelectedPart < High(TCellPart) then begin 237 else if Cursor.SelectedPart < High(TCellPart) then begin
214 Cursor.SelectedPart := Succ(Cursor.SelectedPart); 238 Cursor.SelectedPart := Succ(Cursor.SelectedPart);
215 end; 239 end;
240 DigitInputting := False;
216 end; 241 end;
217 else begin 242 else begin
218 with Cells[Cursor.X, Cursor.Y] do begin 243 case Cursor.SelectedPart of
219 Instrument := Random(20); 244 cpNote: InputNote(Key);
220 EffectCode := Random($F); 245 cpInstrument: InputInstrument(Key);
221 EffectParams := Random($FF); 246 cpVolume: InputVolume(Key);
222 Note := NoteMap.Keys[Random(NoteMap.Count)]; 247 cpEffectCode: InputEffectCode(Key);
248 cpEffectParams: InputEffectParams(Key);
223 end; 249 end;
224 end; 250 end;
225 end; 251 end;
@@ -251,7 +277,7 @@ var
251 begin 277 begin
252 R := SelectionsToRect(Cursor, Other); 278 R := SelectionsToRect(Cursor, Other);
253 279
254 with Cursor do begin; 280 with Cursor do
255 BitBlt( 281 BitBlt(
256 Canvas.Handle, 282 Canvas.Handle,
257 R.Left, 283 R.Left,
@@ -262,7 +288,6 @@ begin
262 R.Left, 288 R.Left,
263 R.Top, 289 R.Top,
264 DSTINVERT); 290 DSTINVERT);
265 end;
266 end; 291 end;
267 292
268 procedure TTrackerGrid.ClampCursors; 293 procedure TTrackerGrid.ClampCursors;
@@ -273,6 +298,65 @@ begin
273 Other.X := Min(3, Max(0, Other.X)); 298 Other.X := Min(3, Max(0, Other.X));
274 end; 299 end;
275 300
301 procedure TTrackerGrid.InputNote(Key: Word);
302 var
303 Temp: Integer;
304 begin
305 with Cells[Cursor.X, Cursor.Y] do
306 if Key = VK_DELETE then
307 Note := NO_NOTE
308 else begin
309 Keybindings.TryGetData(Key, Temp);
310 if Temp <> 0 then Note := Temp;
311 end;
312 end;
313
314 procedure TTrackerGrid.InputInstrument(Key: Word);
315 var
316 Temp: Byte;
317 begin
318 with Cells[Cursor.X, Cursor.Y] do
319 if Key = VK_DELETE then Instrument := 0
320 else if KeycodeToHexNumber(Key, Temp) and (Temp < 9) then
321 Instrument := ((Instrument mod 10) * 10) + Temp;
322 end;
323
324 procedure TTrackerGrid.InputVolume(Key: Word);
325 begin
326
327 end;
328
329 procedure TTrackerGrid.InputEffectCode(Key: Word);
330 begin
331 with Cells[Cursor.X, Cursor.Y] do
332 if Key = VK_DELETE then begin
333 EffectCode := 0;
334 EffectParams.Value := 0;
335 end
336 else KeycodeToHexNumber(Key, EffectCode);
337 end;
338
339 procedure TTrackerGrid.InputEffectParams(Key: Word);
340 var
341 Temp: Byte;
342 begin
343 with Cells[Cursor.X, Cursor.Y] do
344 if Key = VK_DELETE then begin
345 EffectCode := 0;
346 EffectParams.Value := 0;
347 end
348 else if not DigitInputting then begin
349 if KeycodeToHexNumber(Key, Temp) then begin
350 EffectParams.Param2 := EffectParams.Param1;
351 EffectParams.Param1 := Temp;
352 DigitInputting := True;
353 end;
354 end
355 else
356 if KeycodeToHexNumber(Key, EffectParams.Param2) then
357 DigitInputting := False;
358 end;
359
276 procedure TTrackerGrid.RenderRow(Row: Integer); 360 procedure TTrackerGrid.RenderRow(Row: Integer);
277 var 361 var
278 I: Integer; 362 I: Integer;
@@ -315,12 +399,14 @@ begin
315 Font.Color := clGray; 399 Font.Color := clGray;
316 TextOut(PenPos.X, PenPos.Y, '...'); 400 TextOut(PenPos.X, PenPos.Y, '...');
317 401
318 if (Cell.EffectCode <> 0) or (Cell.EffectParams <> 0) then begin 402 if (Cell.EffectCode <> 0) or (Cell.EffectParams.Value <> 0) then begin
319 Font.Color := GetEffectColor(Cell.EffectCode); 403 Font.Color := GetEffectColor(Cell.EffectCode);
320 TextOut( 404 TextOut(
321 PenPos.X, 405 PenPos.X,
322 PenPos.Y, 406 PenPos.Y,
323 IntToHex(Cell.EffectCode, 1) + IntToHex(Cell.EffectParams, 2) 407 IntToHex(Cell.EffectCode, 1)
408 + IntToHex(Cell.EffectParams.Param1, 1)
409 + IntToHex(Cell.EffectParams.Param2, 1)
324 ); 410 );
325 end 411 end
326 else begin 412 else begin
@@ -378,8 +464,12 @@ begin
378 CharLeft := 6; 464 CharLeft := 6;
379 CharRight := 9; 465 CharRight := 9;
380 end; 466 end;
381 cpEffect: begin 467 cpEffectCode: begin
382 CharLeft := 9; 468 CharLeft := 9;
469 CharRight := 10;
470 end;
471 cpEffectParams: begin
472 CharLeft := 10;
383 CharRight := 12; 473 CharRight := 12;
384 end; 474 end;
385 end; 475 end;
@@ -399,11 +489,45 @@ begin
399 X := Trunc((X/ColumnWidth)*13); 489 X := Trunc((X/ColumnWidth)*13);
400 case X of 490 case X of
401 0..3: Result.SelectedPart := cpNote; 491 0..3: Result.SelectedPart := cpNote;
402 4..6: Result.SelectedPart := cpInstrument; 492 4..5: Result.SelectedPart := cpInstrument;
403 7..9: Result.SelectedPart := cpVolume; 493 6..8: Result.SelectedPart := cpVolume;
404 10..12: Result.SelectedPart := cpEffect; 494 9: Result.SelectedPart := cpEffectCode;
495 10..12: Result.SelectedPart := cpEffectParams;
405 end; 496 end;
406 end; 497 end;
407 498
499 function TTrackerGrid.KeycodeToHexNumber(Key: Word; out Num: Byte): Boolean;
500 begin
501 Result := True;
502
503 case Key of
504 VK_0: Num := $0;
505 VK_1: Num := $1;
506 VK_2: Num := $2;
507 VK_3: Num := $3;
508 VK_4: Num := $4;
509 VK_5: Num := $5;
510 VK_6: Num := $6;
511 VK_7: Num := $7;
512 VK_8: Num := $8;
513 VK_9: Num := $9;
514 VK_A: Num := $A;
515 VK_B: Num := $B;
516 VK_C: Num := $C;
517 VK_D: Num := $D;
518 VK_E: Num := $E;
519 VK_F: Num := $F;
520 else Result := False;
521 end;
522 end;
523
524 function TTrackerGrid.KeycodeToHexNumber(Key: Word; out Num: Integer): Boolean;
525 var
526 X: Byte;
527 begin
528 Result := KeycodeToHexNumber(Key, X);
529 Num := X;
530 end;
531
408 end. 532 end.
409 533