1 // for finding memory leaks in debug mode with Visual Studio
2 #if defined _DEBUG && defined _MSC_VER
3 #include <crtdbg.h>
4 #endif
5
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <stdbool.h>
9 #include "ft2_header.h"
10 #include "ft2_config.h"
11 #include "ft2_pattern_ed.h"
12 #include "ft2_gui.h"
13 #include "ft2_sample_ed.h"
14 #include "ft2_pattern_draw.h"
15 #include "ft2_inst_ed.h"
16 #include "scopes/ft2_scopes.h"
17 #include "ft2_diskop.h"
18 #include "ft2_audio.h"
19 #include "ft2_wav_renderer.h"
20 #include "ft2_mouse.h"
21 #include "ft2_video.h"
22 #include "ft2_tables.h"
23 #include "ft2_bmp.h"
24 #include "ft2_structs.h"
25
26 pattMark_t pattMark; // globalized
27
28 // for pattern marking w/ keyboard
29 static int8_t lastChMark;
30 static int16_t lastRowMark;
31
32 // for pattern marking w/ mouse
33 static int32_t lastMarkX1 = -1, lastMarkX2 = -1, lastMarkY1 = -1, lastMarkY2 = -1;
34
35 static const uint8_t ptnNumRows[8] = { 27, 25, 20, 19, 42, 40, 31, 30 };
36 static const uint8_t ptnLineSub[8] = { 13, 12, 9, 9, 20, 19, 15, 14 };
37 static const uint8_t iSwitchExtW[4] = { 40, 40, 40, 39 };
38 static const uint8_t iSwitchExtY[8] = { 2, 2, 2, 2, 19, 19, 19, 19 };
39 static const uint8_t iSwitchY[8] = { 2, 19, 36, 53, 73, 90, 107, 124 };
40 static const uint16_t iSwitchExtX[4] = { 221, 262, 303, 344 };
41
42 static int32_t lastMouseX, lastMouseY;
43 static int32_t last_TimeH, last_TimeM, last_TimeS;
44
allocatePattern(uint16_t pattNum)45 bool allocatePattern(uint16_t pattNum) // for tracker use only, not in loader!
46 {
47 const bool audioWasntLocked = !audio.locked;
48 if (audioWasntLocked)
49 lockAudio();
50
51 if (pattern[pattNum] == NULL)
52 {
53 /* Original FT2 allocates only the amount of rows needed, but we don't
54 ** do that to avoid out of bondary row look-up between out-of-sync replayer
55 ** state and tracker state (yes it used to happen, rarely). We're not wasting
56 ** too much RAM for a modern computer anyway. Worst case: 256 allocated
57 ** patterns would be ~10MB.
58 **/
59
60 pattern[pattNum] = (note_t *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
61 if (pattern[pattNum] == NULL)
62 {
63 if (audioWasntLocked)
64 unlockAudio();
65
66 return false;
67 }
68
69 song.currNumRows = patternNumRows[pattNum];
70 }
71
72 if (audioWasntLocked)
73 unlockAudio();
74
75 return true;
76 }
77
killPatternIfUnused(uint16_t pattNum)78 void killPatternIfUnused(uint16_t pattNum) // for tracker use only, not in loader!
79 {
80 const bool audioWasntLocked = !audio.locked;
81 if (audioWasntLocked)
82 lockAudio();
83
84 if (patternEmpty(pattNum))
85 {
86 if (pattern[pattNum] != NULL)
87 {
88 free(pattern[pattNum]);
89 pattern[pattNum] = NULL;
90 }
91 }
92
93 if (audioWasntLocked)
94 unlockAudio();
95 }
96
getMaxVisibleChannels(void)97 uint8_t getMaxVisibleChannels(void)
98 {
99 assert(config.ptnMaxChannels >= 0 && config.ptnMaxChannels <= 3);
100 if (config.ptnShowVolColumn)
101 return maxVisibleChans1[config.ptnMaxChannels];
102 else
103 return maxVisibleChans2[config.ptnMaxChannels];
104 }
105
updatePatternWidth(void)106 void updatePatternWidth(void)
107 {
108 if (ui.numChannelsShown > ui.maxVisibleChannels)
109 ui.numChannelsShown = ui.maxVisibleChannels;
110
111 assert(ui.numChannelsShown >= 2 && ui.numChannelsShown <= 12);
112
113 ui.patternChannelWidth = chanWidths[(ui.numChannelsShown / 2) - 1] + 3;
114 }
115
updateAdvEdit(void)116 void updateAdvEdit(void)
117 {
118 hexOutBg(92, 113, PAL_FORGRND, PAL_DESKTOP, editor.srcInstr, 2);
119 hexOutBg(92, 126, PAL_FORGRND, PAL_DESKTOP, editor.curInstr, 2);
120 }
121
setAdvEditCheckBoxes(void)122 void setAdvEditCheckBoxes(void)
123 {
124 checkBoxes[CB_ENABLE_MASKING].checked = editor.copyMaskEnable;
125 checkBoxes[CB_COPY_MASK_0].checked = editor.copyMask[0];
126 checkBoxes[CB_COPY_MASK_1].checked = editor.copyMask[1];
127 checkBoxes[CB_COPY_MASK_2].checked = editor.copyMask[2];
128 checkBoxes[CB_COPY_MASK_3].checked = editor.copyMask[3];
129 checkBoxes[CB_COPY_MASK_4].checked = editor.copyMask[4];
130 checkBoxes[CB_PASTE_MASK_0].checked = editor.pasteMask[0];
131 checkBoxes[CB_PASTE_MASK_1].checked = editor.pasteMask[1];
132 checkBoxes[CB_PASTE_MASK_2].checked = editor.pasteMask[2];
133 checkBoxes[CB_PASTE_MASK_3].checked = editor.pasteMask[3];
134 checkBoxes[CB_PASTE_MASK_4].checked = editor.pasteMask[4];
135 checkBoxes[CB_TRANSP_MASK_0].checked = editor.transpMask[0];
136 checkBoxes[CB_TRANSP_MASK_1].checked = editor.transpMask[1];
137 checkBoxes[CB_TRANSP_MASK_2].checked = editor.transpMask[2];
138 checkBoxes[CB_TRANSP_MASK_3].checked = editor.transpMask[3];
139 checkBoxes[CB_TRANSP_MASK_4].checked = editor.transpMask[4];
140
141 showCheckBox(CB_ENABLE_MASKING);
142 showCheckBox(CB_COPY_MASK_0);
143 showCheckBox(CB_COPY_MASK_1);
144 showCheckBox(CB_COPY_MASK_2);
145 showCheckBox(CB_COPY_MASK_3);
146 showCheckBox(CB_COPY_MASK_4);
147 showCheckBox(CB_PASTE_MASK_0);
148 showCheckBox(CB_PASTE_MASK_1);
149 showCheckBox(CB_PASTE_MASK_2);
150 showCheckBox(CB_PASTE_MASK_3);
151 showCheckBox(CB_PASTE_MASK_4);
152 showCheckBox(CB_TRANSP_MASK_0);
153 showCheckBox(CB_TRANSP_MASK_1);
154 showCheckBox(CB_TRANSP_MASK_2);
155 showCheckBox(CB_TRANSP_MASK_3);
156 showCheckBox(CB_TRANSP_MASK_4);
157 }
158
drawAdvEdit(void)159 void drawAdvEdit(void)
160 {
161 drawFramework( 0, 92, 110, 17, FRAMEWORK_TYPE1);
162 drawFramework( 0, 109, 110, 64, FRAMEWORK_TYPE1);
163 drawFramework(110, 92, 124, 81, FRAMEWORK_TYPE1);
164 drawFramework(234, 92, 19, 81, FRAMEWORK_TYPE1);
165 drawFramework(253, 92, 19, 81, FRAMEWORK_TYPE1);
166 drawFramework(272, 92, 19, 81, FRAMEWORK_TYPE1);
167
168 textOutShadow( 4, 96, PAL_FORGRND, PAL_DSKTOP2, "Instr. remap:");
169 textOutShadow( 4, 113, PAL_FORGRND, PAL_DSKTOP2, "Old number");
170 textOutShadow( 4, 126, PAL_FORGRND, PAL_DSKTOP2, "New number");
171 textOutShadow(129, 96, PAL_FORGRND, PAL_DSKTOP2, "Masking enable");
172 textOutShadow(114, 109, PAL_FORGRND, PAL_DSKTOP2, "Note");
173 textOutShadow(114, 122, PAL_FORGRND, PAL_DSKTOP2, "Instrument number");
174 textOutShadow(114, 135, PAL_FORGRND, PAL_DSKTOP2, "Volume column");
175 textOutShadow(114, 148, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 1");
176 textOutShadow(114, 161, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 2,3");
177
178 charOutShadow(239, 95, PAL_FORGRND, PAL_DSKTOP2, 'C');
179 charOutShadow(258, 95, PAL_FORGRND, PAL_DSKTOP2, 'P');
180 charOutShadow(277, 95, PAL_FORGRND, PAL_DSKTOP2, 'T');
181
182 showPushButton(PB_REMAP_TRACK);
183 showPushButton(PB_REMAP_PATTERN);
184 showPushButton(PB_REMAP_SONG);
185 showPushButton(PB_REMAP_BLOCK);
186
187 setAdvEditCheckBoxes();
188
189 updateAdvEdit();
190 }
191
hideAdvEdit(void)192 void hideAdvEdit(void)
193 {
194 ui.advEditShown = false;
195
196 hidePushButton(PB_REMAP_TRACK);
197 hidePushButton(PB_REMAP_PATTERN);
198 hidePushButton(PB_REMAP_SONG);
199 hidePushButton(PB_REMAP_BLOCK);
200
201 hideCheckBox(CB_ENABLE_MASKING);
202 hideCheckBox(CB_COPY_MASK_0);
203 hideCheckBox(CB_COPY_MASK_1);
204 hideCheckBox(CB_COPY_MASK_2);
205 hideCheckBox(CB_COPY_MASK_3);
206 hideCheckBox(CB_COPY_MASK_4);
207 hideCheckBox(CB_PASTE_MASK_0);
208 hideCheckBox(CB_PASTE_MASK_1);
209 hideCheckBox(CB_PASTE_MASK_2);
210 hideCheckBox(CB_PASTE_MASK_3);
211 hideCheckBox(CB_PASTE_MASK_4);
212 hideCheckBox(CB_TRANSP_MASK_0);
213 hideCheckBox(CB_TRANSP_MASK_1);
214 hideCheckBox(CB_TRANSP_MASK_2);
215 hideCheckBox(CB_TRANSP_MASK_3);
216 hideCheckBox(CB_TRANSP_MASK_4);
217
218 ui.scopesShown = true;
219 drawScopeFramework();
220 }
221
showAdvEdit(void)222 void showAdvEdit(void)
223 {
224 if (ui.extended)
225 exitPatternEditorExtended();
226
227 hideTopScreen();
228 showTopScreen(false);
229
230 ui.advEditShown = true;
231 ui.scopesShown = false;
232 drawAdvEdit();
233 }
234
toggleAdvEdit(void)235 void toggleAdvEdit(void)
236 {
237 if (ui.advEditShown)
238 hideAdvEdit();
239 else
240 showAdvEdit();
241 }
242
drawTranspose(void)243 void drawTranspose(void)
244 {
245 drawFramework(0, 92, 53, 16, FRAMEWORK_TYPE1);
246 drawFramework(53, 92, 119, 16, FRAMEWORK_TYPE1);
247 drawFramework(172, 92, 119, 16, FRAMEWORK_TYPE1);
248 drawFramework(0, 108, 53, 65, FRAMEWORK_TYPE1);
249 drawFramework(53, 108, 119, 65, FRAMEWORK_TYPE1);
250 drawFramework(172, 108, 119, 65, FRAMEWORK_TYPE1);
251
252 textOutShadow(4, 95, PAL_FORGRND, PAL_DSKTOP2, "Transp.");
253 textOutShadow(58, 95, PAL_FORGRND, PAL_DSKTOP2, "Current instrument");
254 textOutShadow(188, 95, PAL_FORGRND, PAL_DSKTOP2, "All instruments");
255 textOutShadow(4, 114, PAL_FORGRND, PAL_DSKTOP2, "Track");
256 textOutShadow(4, 129, PAL_FORGRND, PAL_DSKTOP2, "Pattern");
257 textOutShadow(4, 144, PAL_FORGRND, PAL_DSKTOP2, "Song");
258 textOutShadow(4, 159, PAL_FORGRND, PAL_DSKTOP2, "Block");
259
260 showPushButton(PB_TRANSP_CUR_INS_TRK_UP);
261 showPushButton(PB_TRANSP_CUR_INS_TRK_DN);
262 showPushButton(PB_TRANSP_CUR_INS_TRK_12UP);
263 showPushButton(PB_TRANSP_CUR_INS_TRK_12DN);
264 showPushButton(PB_TRANSP_ALL_INS_TRK_UP);
265 showPushButton(PB_TRANSP_ALL_INS_TRK_DN);
266 showPushButton(PB_TRANSP_ALL_INS_TRK_12UP);
267 showPushButton(PB_TRANSP_ALL_INS_TRK_12DN);
268 showPushButton(PB_TRANSP_CUR_INS_PAT_UP);
269 showPushButton(PB_TRANSP_CUR_INS_PAT_DN);
270 showPushButton(PB_TRANSP_CUR_INS_PAT_12UP);
271 showPushButton(PB_TRANSP_CUR_INS_PAT_12DN);
272 showPushButton(PB_TRANSP_ALL_INS_PAT_UP);
273 showPushButton(PB_TRANSP_ALL_INS_PAT_DN);
274 showPushButton(PB_TRANSP_ALL_INS_PAT_12UP);
275 showPushButton(PB_TRANSP_ALL_INS_PAT_12DN);
276 showPushButton(PB_TRANSP_CUR_INS_SNG_UP);
277 showPushButton(PB_TRANSP_CUR_INS_SNG_DN);
278 showPushButton(PB_TRANSP_CUR_INS_SNG_12UP);
279 showPushButton(PB_TRANSP_CUR_INS_SNG_12DN);
280 showPushButton(PB_TRANSP_ALL_INS_SNG_UP);
281 showPushButton(PB_TRANSP_ALL_INS_SNG_DN);
282 showPushButton(PB_TRANSP_ALL_INS_SNG_12UP);
283 showPushButton(PB_TRANSP_ALL_INS_SNG_12DN);
284 showPushButton(PB_TRANSP_CUR_INS_BLK_UP);
285 showPushButton(PB_TRANSP_CUR_INS_BLK_DN);
286 showPushButton(PB_TRANSP_CUR_INS_BLK_12UP);
287 showPushButton(PB_TRANSP_CUR_INS_BLK_12DN);
288 showPushButton(PB_TRANSP_ALL_INS_BLK_UP);
289 showPushButton(PB_TRANSP_ALL_INS_BLK_DN);
290 showPushButton(PB_TRANSP_ALL_INS_BLK_12UP);
291 showPushButton(PB_TRANSP_ALL_INS_BLK_12DN);
292 }
293
showTranspose(void)294 void showTranspose(void)
295 {
296 if (ui.extended)
297 exitPatternEditorExtended();
298
299 hideTopScreen();
300 showTopScreen(false);
301
302 ui.transposeShown = true;
303 ui.scopesShown = false;
304 drawTranspose();
305 }
306
hideTranspose(void)307 void hideTranspose(void)
308 {
309 hidePushButton(PB_TRANSP_CUR_INS_TRK_UP);
310 hidePushButton(PB_TRANSP_CUR_INS_TRK_DN);
311 hidePushButton(PB_TRANSP_CUR_INS_TRK_12UP);
312 hidePushButton(PB_TRANSP_CUR_INS_TRK_12DN);
313 hidePushButton(PB_TRANSP_ALL_INS_TRK_UP);
314 hidePushButton(PB_TRANSP_ALL_INS_TRK_DN);
315 hidePushButton(PB_TRANSP_ALL_INS_TRK_12UP);
316 hidePushButton(PB_TRANSP_ALL_INS_TRK_12DN);
317 hidePushButton(PB_TRANSP_CUR_INS_PAT_UP);
318 hidePushButton(PB_TRANSP_CUR_INS_PAT_DN);
319 hidePushButton(PB_TRANSP_CUR_INS_PAT_12UP);
320 hidePushButton(PB_TRANSP_CUR_INS_PAT_12DN);
321 hidePushButton(PB_TRANSP_ALL_INS_PAT_UP);
322 hidePushButton(PB_TRANSP_ALL_INS_PAT_DN);
323 hidePushButton(PB_TRANSP_ALL_INS_PAT_12UP);
324 hidePushButton(PB_TRANSP_ALL_INS_PAT_12DN);
325 hidePushButton(PB_TRANSP_CUR_INS_SNG_UP);
326 hidePushButton(PB_TRANSP_CUR_INS_SNG_DN);
327 hidePushButton(PB_TRANSP_CUR_INS_SNG_12UP);
328 hidePushButton(PB_TRANSP_CUR_INS_SNG_12DN);
329 hidePushButton(PB_TRANSP_ALL_INS_SNG_UP);
330 hidePushButton(PB_TRANSP_ALL_INS_SNG_DN);
331 hidePushButton(PB_TRANSP_ALL_INS_SNG_12UP);
332 hidePushButton(PB_TRANSP_ALL_INS_SNG_12DN);
333 hidePushButton(PB_TRANSP_CUR_INS_BLK_UP);
334 hidePushButton(PB_TRANSP_CUR_INS_BLK_DN);
335 hidePushButton(PB_TRANSP_CUR_INS_BLK_12UP);
336 hidePushButton(PB_TRANSP_CUR_INS_BLK_12DN);
337 hidePushButton(PB_TRANSP_ALL_INS_BLK_UP);
338 hidePushButton(PB_TRANSP_ALL_INS_BLK_DN);
339 hidePushButton(PB_TRANSP_ALL_INS_BLK_12UP);
340 hidePushButton(PB_TRANSP_ALL_INS_BLK_12DN);
341
342 ui.transposeShown = false;
343 ui.scopesShown = true;
344 drawScopeFramework();
345 }
346
toggleTranspose(void)347 void toggleTranspose(void)
348 {
349 if (ui.transposeShown)
350 hideTranspose();
351 else
352 showTranspose();
353 }
354
355 // ----- PATTERN CURSOR FUNCTIONS -----
356
cursorChannelLeft(void)357 void cursorChannelLeft(void)
358 {
359 cursor.object = CURSOR_EFX2;
360
361 if (cursor.ch == 0)
362 {
363 cursor.ch = (uint8_t)(song.numChannels - 1);
364 if (ui.pattChanScrollShown)
365 setScrollBarPos(SB_CHAN_SCROLL, song.numChannels, true);
366 }
367 else
368 {
369 cursor.ch--;
370 if (ui.pattChanScrollShown)
371 {
372 if (cursor.ch < ui.channelOffset)
373 scrollBarScrollUp(SB_CHAN_SCROLL, 1);
374 }
375 }
376 }
377
cursorChannelRight(void)378 void cursorChannelRight(void)
379 {
380 cursor.object = CURSOR_NOTE;
381
382 if (cursor.ch >= song.numChannels-1)
383 {
384 cursor.ch = 0;
385 if (ui.pattChanScrollShown)
386 setScrollBarPos(SB_CHAN_SCROLL, 0, true);
387 }
388 else
389 {
390 cursor.ch++;
391 if (ui.pattChanScrollShown && cursor.ch >= ui.channelOffset+ui.numChannelsShown)
392 scrollBarScrollDown(SB_CHAN_SCROLL, 1);
393 }
394 }
395
cursorTabLeft(void)396 void cursorTabLeft(void)
397 {
398 if (cursor.object == CURSOR_NOTE)
399 cursorChannelLeft();
400
401 cursor.object = CURSOR_NOTE;
402 ui.updatePatternEditor = true;
403 }
404
cursorTabRight(void)405 void cursorTabRight(void)
406 {
407 cursorChannelRight();
408 cursor.object = CURSOR_NOTE;
409 ui.updatePatternEditor = true;
410 }
411
chanLeft(void)412 void chanLeft(void)
413 {
414 cursorChannelLeft();
415 cursor.object = CURSOR_NOTE;
416 ui.updatePatternEditor = true;
417 }
418
chanRight(void)419 void chanRight(void)
420 {
421 cursorChannelRight();
422 cursor.object = CURSOR_NOTE;
423 ui.updatePatternEditor = true;
424 }
425
cursorLeft(void)426 void cursorLeft(void)
427 {
428 cursor.object--;
429
430 if (!config.ptnShowVolColumn)
431 {
432 while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
433 cursor.object--;
434 }
435
436 if (cursor.object == -1)
437 {
438 cursor.object = CURSOR_EFX2;
439 cursorChannelLeft();
440 }
441
442 ui.updatePatternEditor = true;
443 }
444
cursorRight(void)445 void cursorRight(void)
446 {
447 cursor.object++;
448
449 if (!config.ptnShowVolColumn)
450 {
451 while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
452 cursor.object++;
453 }
454
455 if (cursor.object == 8)
456 {
457 cursor.object = CURSOR_NOTE;
458 cursorChannelRight();
459 }
460
461 ui.updatePatternEditor = true;
462 }
463
showPatternEditor(void)464 void showPatternEditor(void)
465 {
466 ui.patternEditorShown = true;
467 updateChanNums();
468 drawPatternBorders();
469 ui.updatePatternEditor = true;
470 }
471
hidePatternEditor(void)472 void hidePatternEditor(void)
473 {
474 hideScrollBar(SB_CHAN_SCROLL);
475 hidePushButton(PB_CHAN_SCROLL_LEFT);
476 hidePushButton(PB_CHAN_SCROLL_RIGHT);
477
478 ui.patternEditorShown = false;
479 }
480
updatePatternEditorGUI(void)481 static void updatePatternEditorGUI(void)
482 {
483 uint16_t i;
484 pushButton_t *p;
485 textBox_t *t;
486
487 if (ui.extended)
488 {
489 // extended pattern editor
490
491 // instrument names
492 t = &textBoxes[TB_INST1];
493 for (i = 0; i < 8; i++, t++)
494 {
495 if (i < 4)
496 {
497 t->x = 406;
498 t->y = 5 + (i * 11);
499 }
500 else
501 {
502 t->x = 529;
503 t->y = 5 + ((i - 4) * 11);
504 }
505
506 t->w = 99;
507 t->renderW = t->w - (t->tx * 2);
508 }
509
510 scrollBars[SB_POS_ED].h = 23;
511
512 pushButtons[PB_POSED_POS_DOWN].y = 38;
513 pushButtons[PB_POSED_PATT_UP].y = 20;
514 pushButtons[PB_POSED_PATT_DOWN].y = 20;
515 pushButtons[PB_POSED_DEL].y = 35;
516 pushButtons[PB_SWAP_BANK].caption = "Swap B.";
517 pushButtons[PB_SWAP_BANK].caption2 = NULL;
518 pushButtons[PB_SWAP_BANK].x = 162;
519 pushButtons[PB_SWAP_BANK].y = 35;
520 pushButtons[PB_SWAP_BANK].w = 53;
521 pushButtons[PB_SWAP_BANK].h = 16;
522 pushButtons[PB_POSED_LEN_UP].x = 180;
523 pushButtons[PB_POSED_LEN_UP].y = 3;
524 pushButtons[PB_POSED_LEN_DOWN].x = 197;
525 pushButtons[PB_POSED_LEN_DOWN].y = 3;
526 pushButtons[PB_POSED_REP_UP].x = 180;
527 pushButtons[PB_POSED_REP_UP].y = 17;
528 pushButtons[PB_POSED_REP_DOWN].x = 197;
529 pushButtons[PB_POSED_REP_DOWN].y = 17;
530 pushButtons[PB_PATT_UP].x = 267;
531 pushButtons[PB_PATT_UP].y = 37;
532 pushButtons[PB_PATT_DOWN].x = 284;
533 pushButtons[PB_PATT_DOWN].y = 37;
534 pushButtons[PB_PATTLEN_UP].x = 348;
535 pushButtons[PB_PATTLEN_UP].y = 37;
536 pushButtons[PB_PATTLEN_DOWN].x = 365;
537 pushButtons[PB_PATTLEN_DOWN].y = 37;
538
539 // instrument switcher
540 p = &pushButtons[PB_RANGE1];
541 for (i = 0; i < 16; i++, p++)
542 {
543 p->w = iSwitchExtW[i & 3];
544 p->x = iSwitchExtX[i & 3];
545 p->y = iSwitchExtY[i & 7];
546 }
547 }
548 else
549 {
550 // instrument names
551 t = &textBoxes[TB_INST1];
552 for (i = 0; i < 8; i++, t++)
553 {
554 t->y = 5 + (i * 11);
555 t->x = 446;
556 t->w = 140;
557 t->renderW = t->w - (t->tx * 2);
558 }
559
560 // normal pattern editor
561
562 scrollBars[SB_POS_ED].h = 21;
563
564 pushButtons[PB_POSED_POS_DOWN].y = 36;
565 pushButtons[PB_POSED_PATT_UP].y = 19;
566 pushButtons[PB_POSED_PATT_DOWN].y = 19;
567 pushButtons[PB_POSED_DEL].y = 33;
568 pushButtons[PB_SWAP_BANK].caption = "Swap";
569 pushButtons[PB_SWAP_BANK].caption2 = "Bank";
570 pushButtons[PB_SWAP_BANK].x = 590;
571 pushButtons[PB_SWAP_BANK].y = 144;
572 pushButtons[PB_SWAP_BANK].w = 39;
573 pushButtons[PB_SWAP_BANK].h = 27;
574 pushButtons[PB_POSED_LEN_UP].x = 74;
575 pushButtons[PB_POSED_LEN_UP].y = 50;
576 pushButtons[PB_POSED_LEN_DOWN].x = 91;
577 pushButtons[PB_POSED_LEN_DOWN].y = 50;
578 pushButtons[PB_POSED_REP_UP].x = 74;
579 pushButtons[PB_POSED_REP_UP].y = 62;
580 pushButtons[PB_POSED_REP_DOWN].x = 91;
581 pushButtons[PB_POSED_REP_DOWN].y = 62;
582 pushButtons[PB_PATT_UP].x = 253;
583 pushButtons[PB_PATT_UP].y = 34;
584 pushButtons[PB_PATT_DOWN].x = 270;
585 pushButtons[PB_PATT_DOWN].y = 34;
586 pushButtons[PB_PATTLEN_UP].x = 253;
587 pushButtons[PB_PATTLEN_UP].y = 48;
588 pushButtons[PB_PATTLEN_DOWN].x = 270;
589 pushButtons[PB_PATTLEN_DOWN].y = 48;
590
591 // instrument switcher
592 p = &pushButtons[PB_RANGE1];
593 for (i = 0; i < 16; i++, p++)
594 {
595 p->w = 39;
596 p->x = 590;
597 p->y = iSwitchY[i & 7];
598 }
599 }
600 }
601
patternEditorExtended(void)602 void patternEditorExtended(void)
603 {
604 // backup old screen flags
605 ui._aboutScreenShown = ui.aboutScreenShown;
606 ui._helpScreenShown = ui.helpScreenShown;
607 ui._configScreenShown = ui.configScreenShown;
608 ui._diskOpShown = ui.diskOpShown;
609 ui._nibblesShown = ui.nibblesShown;
610 ui._transposeShown = ui.transposeShown;
611 ui._instEditorShown = ui.instEditorShown;
612 ui._instEditorExtShown = ui.instEditorExtShown;
613 ui._sampleEditorExtShown = ui.sampleEditorExtShown;
614 ui._patternEditorShown = ui.patternEditorShown;
615 ui._sampleEditorShown = ui.sampleEditorShown;
616 ui._advEditShown= ui.advEditShown;
617 ui._wavRendererShown = ui.wavRendererShown;
618 ui._trimScreenShown = ui.trimScreenShown;
619
620 hideTopScreen();
621 hideSampleEditor();
622 hideInstEditor();
623
624 ui.extended = true;
625 ui.patternEditorShown = true;
626 updatePatternEditorGUI(); // change pattern editor layout (based on ui.extended flag)
627 ui.updatePatternEditor = true; // redraw pattern editor
628
629 drawFramework(0, 0, 112, 53, FRAMEWORK_TYPE1);
630 drawFramework(112, 0, 106, 33, FRAMEWORK_TYPE1);
631 drawFramework(112, 33, 106, 20, FRAMEWORK_TYPE1);
632 drawFramework(218, 0, 168, 53, FRAMEWORK_TYPE1);
633
634 // pos ed. stuff
635
636 drawFramework(2, 2, 51, 20, FRAMEWORK_TYPE2);
637 drawFramework(2, 31, 51, 20, FRAMEWORK_TYPE2);
638
639 showScrollBar(SB_POS_ED);
640
641 showPushButton(PB_POSED_POS_UP);
642 showPushButton(PB_POSED_POS_DOWN);
643 showPushButton(PB_POSED_INS);
644 showPushButton(PB_POSED_PATT_UP);
645 showPushButton(PB_POSED_PATT_DOWN);
646 showPushButton(PB_POSED_DEL);
647 showPushButton(PB_POSED_LEN_UP);
648 showPushButton(PB_POSED_LEN_DOWN);
649 showPushButton(PB_POSED_REP_UP);
650 showPushButton(PB_POSED_REP_DOWN);
651 showPushButton(PB_SWAP_BANK);
652 showPushButton(PB_PATT_UP);
653 showPushButton(PB_PATT_DOWN);
654 showPushButton(PB_PATTLEN_UP);
655 showPushButton(PB_PATTLEN_DOWN);
656
657 showPushButton(PB_EXIT_EXT_PATT);
658
659 textOutShadow(116, 5, PAL_FORGRND, PAL_DSKTOP2, "Sng.len.");
660 textOutShadow(116, 19, PAL_FORGRND, PAL_DSKTOP2, "Repst.");
661 textOutShadow(222, 39, PAL_FORGRND, PAL_DSKTOP2, "Ptn.");
662 textOutShadow(305, 39, PAL_FORGRND, PAL_DSKTOP2, "Ln.");
663
664 ui.instrSwitcherShown = true;
665 showInstrumentSwitcher();
666
667 drawSongLength();
668 drawSongLoopStart();
669 drawEditPattern(editor.editPattern);
670 drawPatternLength(editor.editPattern);
671 drawPosEdNums(editor.songPos);
672 ui.updatePosSections = true;
673
674 // kludge to fix scrollbar thumb when the scrollbar height changes during playback
675 if (songPlaying)
676 setScrollBarPos(SB_POS_ED, editor.songPos, false);
677 }
678
exitPatternEditorExtended(void)679 void exitPatternEditorExtended(void)
680 {
681 ui.extended = false;
682 updatePatternEditorGUI();
683 hidePushButton(PB_EXIT_EXT_PATT);
684
685 // set back top screen button maps
686
687 // set back old screen flags
688 ui.aboutScreenShown = ui._aboutScreenShown;
689 ui.helpScreenShown = ui._helpScreenShown;
690 ui.configScreenShown = ui._configScreenShown;
691 ui.diskOpShown = ui._diskOpShown;
692 ui.nibblesShown = ui._nibblesShown;
693 ui.transposeShown = ui._transposeShown;
694 ui.instEditorShown = ui._instEditorShown;
695 ui.instEditorExtShown = ui._instEditorExtShown;
696 ui.sampleEditorExtShown = ui._sampleEditorExtShown;
697 ui.patternEditorShown = ui._patternEditorShown;
698 ui.sampleEditorShown = ui._sampleEditorShown;
699 ui.advEditShown = ui._advEditShown;
700 ui.wavRendererShown = ui._wavRendererShown;
701 ui.trimScreenShown = ui.trimScreenShown;
702
703 showTopScreen(true);
704 showBottomScreen();
705
706 // kludge to fix scrollbar thumb when the scrollbar height changes during playback
707 if (songPlaying)
708 setScrollBarPos(SB_POS_ED, editor.songPos, false);
709 }
710
togglePatternEditorExtended(void)711 void togglePatternEditorExtended(void)
712 {
713 if (ui.extended)
714 exitPatternEditorExtended();
715 else
716 patternEditorExtended();
717 }
718
clearPattMark(void)719 void clearPattMark(void)
720 {
721 memset(&pattMark, 0, sizeof (pattMark));
722
723 lastMarkX1 = -1;
724 lastMarkX2 = -1;
725 lastMarkY1 = -1;
726 lastMarkY2 = -1;
727 }
728
checkMarkLimits(void)729 void checkMarkLimits(void)
730 {
731 const uint16_t limitY = patternNumRows[editor.editPattern];
732 pattMark.markY1 = CLAMP(pattMark.markY1, 0, limitY);
733 pattMark.markY2 = CLAMP(pattMark.markY2, 0, limitY);
734
735 const uint16_t limitX = (uint16_t)(song.numChannels - 1);
736 pattMark.markX1 = CLAMP(pattMark.markX1, 0, limitX);
737 pattMark.markX2 = CLAMP(pattMark.markX2, 0, limitX);
738
739 // XXX: will probably never happen? FT2 has this in CheckMarkLimits() though...
740 if (pattMark.markX1 > pattMark.markX2)
741 pattMark.markX1 = pattMark.markX2;
742 }
743
mouseXToCh(void)744 static int8_t mouseXToCh(void) // used to get channel num from mouse x (for pattern marking)
745 {
746 assert(ui.patternChannelWidth > 0);
747 if (ui.patternChannelWidth == 0)
748 return 0;
749
750 int32_t mouseX = mouse.x - 29;
751 mouseX = CLAMP(mouseX, 0, 573);
752
753 const int8_t chEnd = (ui.channelOffset + ui.numChannelsShown) - 1;
754
755 int8_t ch = ui.channelOffset + (int8_t)(mouseX / ui.patternChannelWidth);
756 ch = CLAMP(ch, 0, chEnd);
757
758 // in some setups there can be non-used channels to the right, do clamping
759 if (ch >= song.numChannels)
760 ch = (int8_t)(song.numChannels - 1);
761
762 return ch;
763 }
764
mouseYToRow(void)765 static int16_t mouseYToRow(void) // used to get row num from mouse y (for pattern marking)
766 {
767 const pattCoordsMouse_t *pattCoordsMouse = &pattCoordMouseTable[config.ptnStretch][ui.pattChanScrollShown][ui.extended];
768
769 // clamp mouse y to boundaries
770 const int16_t maxY = ui.pattChanScrollShown ? 382 : 396;
771 const int16_t my = (int16_t)CLAMP(mouse.y, pattCoordsMouse->upperRowsY, maxY);
772
773 const uint8_t charHeight = config.ptnStretch ? 11 : 8;
774
775 // test top/middle/bottom rows
776 if (my < pattCoordsMouse->midRowY)
777 {
778 // top rows
779 int16_t row = editor.row - (pattCoordsMouse->numUpperRows - ((my - pattCoordsMouse->upperRowsY) / charHeight));
780 if (row < 0)
781 row = 0;
782
783 return row;
784 }
785 else if (my >= pattCoordsMouse->midRowY && my <= pattCoordsMouse->midRowY+10)
786 {
787 // current row (middle)
788 return editor.row;
789 }
790 else
791 {
792 // bottom rows
793 int16_t row = (editor.row + 1) + ((my - pattCoordsMouse->lowerRowsY) / charHeight);
794
795 // prevent being able to mark the next unseen row on the bottom (in some configurations)
796 const uint8_t mode = (ui.extended * 4) + (config.ptnStretch * 2) + ui.pattChanScrollShown;
797
798 const int16_t maxRow = (ptnNumRows[mode] + (editor.row - ptnLineSub[mode])) - 1;
799 if (row > maxRow)
800 row = maxRow;
801
802 // clamp to pattern length
803 const int16_t patternLen = patternNumRows[editor.editPattern];
804 if (row >= patternLen)
805 row = patternLen - 1;
806
807 return row;
808 }
809 }
810
handlePatternDataMouseDown(bool mouseButtonHeld)811 void handlePatternDataMouseDown(bool mouseButtonHeld)
812 {
813 int16_t y1, y2;
814
815 // non-FT2 feature: Use right mouse button to remove pattern marking
816 if (mouse.rightButtonPressed)
817 {
818 clearPattMark();
819 ui.updatePatternEditor = true;
820 return;
821 }
822
823 if (!mouseButtonHeld)
824 {
825 // we clicked inside the pattern data area for the first time, set initial vars
826
827 mouse.lastUsedObjectType = OBJECT_PATTERNMARK;
828
829 lastMouseX = mouse.x;
830 lastMouseY = mouse.y;
831
832 lastChMark = mouseXToCh();
833 lastRowMark = mouseYToRow();
834
835 pattMark.markX1 = lastChMark;
836 pattMark.markX2 = lastChMark;
837 pattMark.markY1 = lastRowMark;
838 pattMark.markY2 = lastRowMark + 1;
839
840 checkMarkLimits();
841
842 ui.updatePatternEditor = true;
843 return;
844 }
845
846 // we're holding down the mouse button inside the pattern data area
847
848 bool forceMarking = songPlaying;
849
850 // scroll left/right with mouse
851 if (ui.pattChanScrollShown)
852 {
853 if (mouse.x < 29)
854 {
855 scrollBarScrollUp(SB_CHAN_SCROLL, 1);
856 forceMarking = true;
857 }
858 else if (mouse.x > 604)
859 {
860 scrollBarScrollDown(SB_CHAN_SCROLL, 1);
861 forceMarking = true;
862 }
863 }
864
865 // mark channels
866 if (forceMarking || lastMouseX != mouse.x)
867 {
868 lastMouseX = mouse.x;
869
870 int8_t chTmp = mouseXToCh();
871 if (chTmp < lastChMark)
872 {
873 pattMark.markX1 = chTmp;
874 pattMark.markX2 = lastChMark;
875 }
876 else
877 {
878 pattMark.markX2 = chTmp;
879 pattMark.markX1 = lastChMark;
880 }
881
882 if (lastMarkX1 != pattMark.markX1 || lastMarkX2 != pattMark.markX2)
883 {
884 checkMarkLimits();
885 ui.updatePatternEditor = true;
886
887 lastMarkX1 = pattMark.markX1;
888 lastMarkX2 = pattMark.markX2;
889 }
890 }
891
892 // scroll down/up with mouse (if song is not playing)
893 if (!songPlaying)
894 {
895 y1 = ui.extended ? 56 : 176;
896 y2 = ui.pattChanScrollShown ? 382 : 396;
897
898 if (mouse.y < y1)
899 {
900 if (editor.row > 0)
901 setPos(-1, editor.row - 1, true);
902
903 forceMarking = true;
904 ui.updatePatternEditor = true;
905 }
906 else if (mouse.y > y2)
907 {
908 const int16_t numRows = patternNumRows[editor.editPattern];
909 if (editor.row < numRows-1)
910 setPos(-1, editor.row + 1, true);
911
912 forceMarking = true;
913 ui.updatePatternEditor = true;
914 }
915 }
916
917 // mark rows
918 if (forceMarking || lastMouseY != mouse.y)
919 {
920 lastMouseY = mouse.y;
921
922 const int16_t rowTmp = mouseYToRow();
923 if (rowTmp < lastRowMark)
924 {
925 pattMark.markY1 = rowTmp;
926 pattMark.markY2 = lastRowMark + 1;
927 }
928 else
929 {
930 pattMark.markY2 = rowTmp + 1;
931 pattMark.markY1 = lastRowMark;
932 }
933
934 if (lastMarkY1 != pattMark.markY1 || lastMarkY2 != pattMark.markY2)
935 {
936 checkMarkLimits();
937 ui.updatePatternEditor = true;
938
939 lastMarkY1 = pattMark.markY1;
940 lastMarkY2 = pattMark.markY2;
941 }
942 }
943 }
944
rowOneUpWrap(void)945 void rowOneUpWrap(void)
946 {
947 const bool audioWasntLocked = !audio.locked;
948 if (audioWasntLocked)
949 lockAudio();
950
951 song.row = (song.row - 1 + song.currNumRows) % song.currNumRows;
952 if (!songPlaying)
953 {
954 editor.row = (uint8_t)song.row;
955 ui.updatePatternEditor = true;
956 }
957
958 if (audioWasntLocked)
959 unlockAudio();
960 }
961
rowOneDownWrap(void)962 void rowOneDownWrap(void)
963 {
964 const bool audioWasntLocked = !audio.locked;
965 if (audioWasntLocked)
966 lockAudio();
967
968 if (songPlaying)
969 {
970 song.tick = 2;
971 }
972 else
973 {
974 song.row = (song.row + 1 + song.currNumRows) % song.currNumRows;
975 editor.row = (uint8_t)song.row;
976 ui.updatePatternEditor = true;
977 }
978
979 if (audioWasntLocked)
980 unlockAudio();
981 }
982
rowUp(uint16_t amount)983 void rowUp(uint16_t amount)
984 {
985 const bool audioWasntLocked = !audio.locked;
986 if (audioWasntLocked)
987 lockAudio();
988
989 song.row -= amount;
990 if (song.row < 0)
991 song.row = 0;
992
993 if (!songPlaying)
994 {
995 editor.row = (uint8_t)song.row;
996 ui.updatePatternEditor = true;
997 }
998
999 if (audioWasntLocked)
1000 unlockAudio();
1001 }
1002
rowDown(uint16_t amount)1003 void rowDown(uint16_t amount)
1004 {
1005 const bool audioWasntLocked = !audio.locked;
1006 if (audioWasntLocked)
1007 lockAudio();
1008
1009 song.row += amount;
1010 if (song.row >= song.currNumRows)
1011 song.row = song.currNumRows - 1;
1012
1013 if (!songPlaying)
1014 {
1015 editor.row = (uint8_t)song.row;
1016 ui.updatePatternEditor = true;
1017 }
1018
1019 if (audioWasntLocked)
1020 unlockAudio();
1021 }
1022
keybPattMarkUp(void)1023 void keybPattMarkUp(void)
1024 {
1025 int8_t xPos = cursor.ch;
1026 int16_t row = editor.row;
1027
1028 if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
1029 {
1030 pattMark.markX1 = xPos;
1031 pattMark.markX2 = xPos;
1032 pattMark.markY1 = row;
1033 pattMark.markY2 = row + 1;
1034 }
1035
1036 if (row == pattMark.markY1-1)
1037 {
1038 pattMark.markY1 = row;
1039 }
1040 else if (row == pattMark.markY2)
1041 {
1042 pattMark.markY2 = row - 1;
1043 }
1044 else if (row != pattMark.markY1 && row != pattMark.markY2)
1045 {
1046 pattMark.markX1 = xPos;
1047 pattMark.markX2 = xPos;
1048 pattMark.markY1 = row;
1049 pattMark.markY2 = row + 1;
1050
1051 }
1052
1053 checkMarkLimits();
1054 rowOneUpWrap();
1055 }
1056
keybPattMarkDown(void)1057 void keybPattMarkDown(void)
1058 {
1059 int8_t xPos = cursor.ch;
1060 int16_t row = editor.row;
1061
1062 if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
1063 {
1064 pattMark.markX1 = xPos;
1065 pattMark.markX2 = xPos;
1066 pattMark.markY1 = row;
1067 pattMark.markY2 = row + 1;
1068 }
1069
1070 if (row == pattMark.markY2)
1071 {
1072 pattMark.markY2 = row + 1;
1073 }
1074 else if (row == pattMark.markY1-1)
1075 {
1076 pattMark.markY1 = row + 2;
1077 }
1078 else if (row != pattMark.markY1 && row != pattMark.markY2)
1079 {
1080 pattMark.markX1 = xPos;
1081 pattMark.markX2 = xPos;
1082 pattMark.markY1 = row;
1083 pattMark.markY2 = row + 1;
1084 }
1085
1086 checkMarkLimits();
1087 rowOneDownWrap();
1088 }
1089
keybPattMarkLeft(void)1090 void keybPattMarkLeft(void)
1091 {
1092 int8_t xPos = cursor.ch;
1093 int16_t row = editor.row;
1094
1095 if (row != pattMark.markY1-1 && row != pattMark.markY2)
1096 {
1097 pattMark.markY1 = row - 1;
1098 pattMark.markY2 = row;
1099 }
1100
1101 if (xPos == pattMark.markX1)
1102 {
1103 pattMark.markX1 = xPos - 1;
1104 }
1105 else if (xPos == pattMark.markX2)
1106 {
1107 pattMark.markX2 = xPos - 1;
1108 }
1109 else if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
1110 {
1111 pattMark.markX1 = xPos - 1;
1112 pattMark.markX2 = xPos;
1113 pattMark.markY1 = row - 1;
1114 pattMark.markY2 = row;
1115 }
1116
1117 checkMarkLimits();
1118 chanLeft();
1119 }
1120
keybPattMarkRight(void)1121 void keybPattMarkRight(void)
1122 {
1123 int8_t xPos = cursor.ch;
1124 int16_t row = editor.row;
1125
1126 if (row != pattMark.markY1-1 && row != pattMark.markY2)
1127 {
1128 pattMark.markY1 = row - 1;
1129 pattMark.markY2 = row;
1130 }
1131
1132 if (xPos == pattMark.markX2)
1133 {
1134 pattMark.markX2 = xPos + 1;
1135 }
1136 else if (xPos == pattMark.markX1)
1137 {
1138 pattMark.markX1 = xPos + 1;
1139 }
1140 else if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
1141 {
1142 pattMark.markX1 = xPos;
1143 pattMark.markX2 = xPos + 1;
1144 pattMark.markY1 = row - 1;
1145 pattMark.markY2 = row;
1146 }
1147
1148 checkMarkLimits();
1149 chanRight();
1150 }
1151
loadTrack(UNICHAR * filenameU)1152 bool loadTrack(UNICHAR *filenameU)
1153 {
1154 note_t loadBuff[MAX_PATT_LEN];
1155 xtHdr_t h;
1156
1157 FILE *f = UNICHAR_FOPEN(filenameU, "rb");
1158 if (f == NULL)
1159 {
1160 okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1161 return false;
1162 }
1163
1164 if (fread(&h, 1, sizeof (h), f) != sizeof (h))
1165 {
1166 okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1167 goto trackLoadError;
1168 }
1169
1170 if (h.version != 1)
1171 {
1172 okBox(0, "System message", "Incompatible format version!");
1173 goto trackLoadError;
1174 }
1175
1176 if (h.numRows > MAX_PATT_LEN)
1177 h.numRows = MAX_PATT_LEN;
1178
1179 int16_t numRows = patternNumRows[editor.editPattern];
1180 if (numRows > h.numRows)
1181 numRows = h.numRows;
1182
1183 if (fread(loadBuff, numRows * sizeof (note_t), 1, f) != 1)
1184 {
1185 okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1186 goto trackLoadError;
1187 }
1188
1189 if (!allocatePattern(editor.editPattern))
1190 {
1191 okBox(0, "System message", "Not enough memory!");
1192 goto trackLoadError;
1193 }
1194
1195 lockMixerCallback();
1196 for (int32_t i = 0; i < numRows; i++)
1197 {
1198 note_t *p = &pattern[editor.editPattern][(i * MAX_CHANNELS) + cursor.ch];
1199
1200 *p = loadBuff[i];
1201
1202 // sanitize stuff (FT2 doesn't do this!)
1203
1204 if (p->note > 97)
1205 p->note = 0;
1206
1207 if (p->instr > 128)
1208 p->instr = 0;
1209
1210 if (p->efx > 35)
1211 {
1212 p->efx = 0;
1213 p->efxData = 0;
1214 }
1215 }
1216 unlockMixerCallback();
1217
1218 fclose(f);
1219
1220 ui.updatePatternEditor = true;
1221 ui.updatePosSections = true;
1222
1223 diskOpSetFilename(DISKOP_ITEM_TRACK, filenameU);
1224 setSongModifiedFlag();
1225
1226 return true;
1227
1228 trackLoadError:
1229 fclose(f);
1230 return false;
1231 }
1232
saveTrack(UNICHAR * filenameU)1233 bool saveTrack(UNICHAR *filenameU)
1234 {
1235 note_t saveBuff[MAX_PATT_LEN];
1236 xtHdr_t h;
1237
1238 note_t *p = pattern[editor.editPattern];
1239 if (p == NULL)
1240 {
1241 okBox(0, "System message", "The current pattern is empty!");
1242 return false;
1243 }
1244
1245 FILE *f = UNICHAR_FOPEN(filenameU, "wb");
1246 if (f == NULL)
1247 {
1248 okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1249 return false;
1250 }
1251
1252 h.version = 1;
1253 h.numRows = patternNumRows[editor.editPattern];
1254
1255 for (int32_t i = 0; i < h.numRows; i++)
1256 saveBuff[i] = p[(i * MAX_CHANNELS) + cursor.ch];
1257
1258 if (fwrite(&h, sizeof (h), 1, f) != 1)
1259 {
1260 fclose(f);
1261 okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1262 return false;
1263 }
1264
1265 if (fwrite(saveBuff, h.numRows * sizeof (note_t), 1, f) != 1)
1266 {
1267 fclose(f);
1268 okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1269 return false;
1270 }
1271
1272 fclose(f);
1273 return true;
1274 }
1275
loadPattern(UNICHAR * filenameU)1276 bool loadPattern(UNICHAR *filenameU)
1277 {
1278 xpHdr_t h;
1279
1280 FILE *f = UNICHAR_FOPEN(filenameU, "rb");
1281 if (f == NULL)
1282 {
1283 okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1284 return false;
1285 }
1286
1287 if (!allocatePattern(editor.editPattern))
1288 {
1289 okBox(0, "System message", "Not enough memory!");
1290 goto loadPattError;
1291 }
1292
1293 if (fread(&h, 1, sizeof (h), f) != sizeof (h))
1294 {
1295 okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1296 goto loadPattError;
1297 }
1298
1299 if (h.version != 1)
1300 {
1301 okBox(0, "System message", "Incompatible format version!");
1302 goto loadPattError;
1303 }
1304
1305 if (h.numRows > MAX_PATT_LEN)
1306 h.numRows = MAX_PATT_LEN;
1307
1308 lockMixerCallback();
1309
1310 note_t *p = pattern[editor.editPattern];
1311 if (fread(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
1312 {
1313 unlockMixerCallback();
1314 okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1315 goto loadPattError;
1316 }
1317
1318 // sanitize data (FT2 doesn't do this!)
1319 for (int32_t row = 0; row < h.numRows; row++)
1320 {
1321 for (int32_t ch = 0; ch < MAX_CHANNELS; ch++)
1322 {
1323 p = &pattern[editor.editPattern][(row * MAX_CHANNELS) + ch];
1324
1325 if (p->note > 97)
1326 p->note = 0;
1327
1328 if (p->instr > 128)
1329 p->instr = 128;
1330
1331 if (p->efx > 35)
1332 {
1333 p->efx = 0;
1334 p->efxData = 0;
1335 }
1336 }
1337 }
1338
1339 // set new pattern length (FT2 doesn't do this, strange...)
1340 song.currNumRows = patternNumRows[editor.editPattern] = h.numRows;
1341
1342 if (song.row >= song.currNumRows)
1343 {
1344 song.row = song.currNumRows-1;
1345 if (!songPlaying)
1346 editor.row = song.row;
1347 }
1348
1349 unlockMixerCallback();
1350
1351 fclose(f);
1352
1353 ui.updatePatternEditor = true;
1354 ui.updatePosSections = true;
1355
1356 diskOpSetFilename(DISKOP_ITEM_PATTERN, filenameU);
1357 setSongModifiedFlag();
1358
1359 return true;
1360
1361 loadPattError:
1362 fclose(f);
1363 return false;
1364 }
1365
savePattern(UNICHAR * filenameU)1366 bool savePattern(UNICHAR *filenameU)
1367 {
1368 xpHdr_t h;
1369
1370 note_t *p = pattern[editor.editPattern];
1371 if (p == NULL)
1372 {
1373 okBox(0, "System message", "The current pattern is empty!");
1374 return false;
1375 }
1376
1377 FILE *f = UNICHAR_FOPEN(filenameU, "wb");
1378 if (f == NULL)
1379 {
1380 okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1381 return false;
1382 }
1383
1384 h.version = 1;
1385 h.numRows = patternNumRows[editor.editPattern];
1386
1387 if (fwrite(&h, 1, sizeof (h), f) != sizeof (h))
1388 {
1389 fclose(f);
1390 okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1391 return false;
1392 }
1393
1394 if (fwrite(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
1395 {
1396 fclose(f);
1397 okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1398 return false;
1399 }
1400
1401 fclose(f);
1402 return true;
1403 }
1404
scrollChannelLeft(void)1405 void scrollChannelLeft(void)
1406 {
1407 scrollBarScrollLeft(SB_CHAN_SCROLL, 1);
1408 }
1409
scrollChannelRight(void)1410 void scrollChannelRight(void)
1411 {
1412 scrollBarScrollRight(SB_CHAN_SCROLL, 1);
1413 }
1414
setChannelScrollPos(uint32_t pos)1415 void setChannelScrollPos(uint32_t pos)
1416 {
1417 if (!ui.pattChanScrollShown)
1418 {
1419 ui.channelOffset = 0;
1420 return;
1421 }
1422
1423 if (ui.channelOffset == (uint8_t)pos)
1424 return;
1425
1426 ui.channelOffset = (uint8_t)pos;
1427
1428 assert(song.numChannels > ui.numChannelsShown);
1429 if (ui.channelOffset >= song.numChannels-ui.numChannelsShown)
1430 ui.channelOffset = (uint8_t)(song.numChannels-ui.numChannelsShown);
1431
1432 if (cursor.ch >= ui.channelOffset+ui.numChannelsShown)
1433 {
1434 cursor.object = CURSOR_NOTE;
1435 cursor.ch = (ui.channelOffset + ui.numChannelsShown) - 1;
1436 }
1437 else if (cursor.ch < ui.channelOffset)
1438 {
1439 cursor.object = CURSOR_NOTE;
1440 cursor.ch = ui.channelOffset;
1441 }
1442
1443 ui.updatePatternEditor = true;
1444 }
1445
jumpToChannel(uint8_t chNr)1446 void jumpToChannel(uint8_t chNr) // for ALT+q..i ALT+a..k
1447 {
1448 if (ui.sampleEditorShown || ui.instEditorShown)
1449 return;
1450
1451 chNr %= song.numChannels;
1452 if (cursor.ch == chNr)
1453 return;
1454
1455 if (ui.pattChanScrollShown)
1456 {
1457 assert(song.numChannels > ui.numChannelsShown);
1458
1459 if (chNr >= ui.channelOffset+ui.numChannelsShown)
1460 scrollBarScrollDown(SB_CHAN_SCROLL, (chNr - (ui.channelOffset + ui.numChannelsShown)) + 1);
1461 else if (chNr < ui.channelOffset)
1462 scrollBarScrollUp(SB_CHAN_SCROLL, ui.channelOffset - chNr);
1463 }
1464
1465 cursor.ch = chNr; // set it here since scrollBarScrollX() changes it...
1466 ui.updatePatternEditor = true;
1467 }
1468
sbPosEdPos(uint32_t pos)1469 void sbPosEdPos(uint32_t pos)
1470 {
1471 const bool audioWasntLocked = !audio.locked;
1472 if (audioWasntLocked)
1473 lockAudio();
1474
1475 if (song.songPos != (int16_t)pos)
1476 setNewSongPos((int16_t)pos);
1477
1478 if (audioWasntLocked)
1479 unlockAudio();
1480 }
1481
pbPosEdPosUp(void)1482 void pbPosEdPosUp(void)
1483 {
1484 incSongPos();
1485 }
1486
pbPosEdPosDown(void)1487 void pbPosEdPosDown(void)
1488 {
1489 decSongPos();
1490 }
1491
pbPosEdIns(void)1492 void pbPosEdIns(void)
1493 {
1494 if (song.songLength >= 255)
1495 return;
1496
1497 lockMixerCallback();
1498
1499 const uint8_t oldPatt = song.orders[song.songPos];
1500 for (uint16_t i = 0; i < 255-song.songPos; i++)
1501 song.orders[255-i] = song.orders[254-i];
1502 song.orders[song.songPos] = oldPatt;
1503
1504 song.songLength++;
1505
1506 ui.updatePosSections = true;
1507 ui.updatePosEdScrollBar = true;
1508 setSongModifiedFlag();
1509
1510 unlockMixerCallback();
1511 }
1512
pbPosEdDel(void)1513 void pbPosEdDel(void)
1514 {
1515 if (song.songLength <= 1)
1516 return;
1517
1518 lockMixerCallback();
1519
1520 if (song.songPos < 254)
1521 {
1522 for (uint16_t i = 0; i < 254-song.songPos; i++)
1523 song.orders[song.songPos+i] = song.orders[song.songPos+1+i];
1524 }
1525
1526 song.songLength--;
1527 if (song.songLoopStart >= song.songLength)
1528 song.songLoopStart = song.songLength - 1;
1529
1530 if (song.songPos > song.songLength-1)
1531 {
1532 editor.songPos = song.songPos = song.songLength-1;
1533 setPos(song.songPos, -1, false);
1534 }
1535
1536 ui.updatePosSections = true;
1537 ui.updatePosEdScrollBar = true;
1538 setSongModifiedFlag();
1539
1540 unlockMixerCallback();
1541 }
1542
pbPosEdPattUp(void)1543 void pbPosEdPattUp(void)
1544 {
1545 if (song.orders[song.songPos] == 255)
1546 return;
1547
1548 lockMixerCallback();
1549 if (song.orders[song.songPos] < 255)
1550 {
1551 song.orders[song.songPos]++;
1552 song.pattNum = song.orders[song.songPos];
1553
1554 song.currNumRows = patternNumRows[song.pattNum];
1555 if (song.row >= song.currNumRows)
1556 {
1557 song.row = song.currNumRows-1;
1558 if (!songPlaying)
1559 editor.row = song.row;
1560 }
1561
1562 if (!songPlaying)
1563 editor.editPattern = (uint8_t)song.pattNum;
1564
1565 checkMarkLimits();
1566 ui.updatePatternEditor = true;
1567 ui.updatePosSections = true;
1568
1569 setSongModifiedFlag();
1570 }
1571 unlockMixerCallback();
1572 }
1573
pbPosEdPattDown(void)1574 void pbPosEdPattDown(void)
1575 {
1576 if (song.orders[song.songPos] == 0)
1577 return;
1578
1579 lockMixerCallback();
1580 if (song.orders[song.songPos] > 0)
1581 {
1582 song.orders[song.songPos]--;
1583 song.pattNum = song.orders[song.songPos];
1584
1585 song.currNumRows = patternNumRows[song.pattNum];
1586 if (song.row >= song.currNumRows)
1587 {
1588 song.row = song.currNumRows-1;
1589 if (!songPlaying)
1590 editor.row = song.row;
1591 }
1592
1593 if (!songPlaying)
1594 editor.editPattern = (uint8_t)song.pattNum;
1595
1596 checkMarkLimits();
1597 ui.updatePatternEditor = true;
1598 ui.updatePosSections = true;
1599
1600 setSongModifiedFlag();
1601 }
1602 unlockMixerCallback();
1603 }
1604
pbPosEdLenUp(void)1605 void pbPosEdLenUp(void)
1606 {
1607 if (song.songLength >= 255)
1608 return;
1609
1610 const bool audioWasntLocked = !audio.locked;
1611 if (audioWasntLocked)
1612 lockAudio();
1613
1614 song.songLength++;
1615
1616 ui.updatePosSections = true;
1617 ui.updatePosEdScrollBar = true;
1618 setSongModifiedFlag();
1619
1620 if (audioWasntLocked)
1621 unlockAudio();
1622 }
1623
pbPosEdLenDown(void)1624 void pbPosEdLenDown(void)
1625 {
1626 if (song.songLength <= 1)
1627 return;
1628
1629 const bool audioWasntLocked = !audio.locked;
1630 if (audioWasntLocked)
1631 lockAudio();
1632
1633 song.songLength--;
1634 if (song.songLoopStart >= song.songLength)
1635 song.songLoopStart = song.songLength - 1;
1636
1637 if (song.songPos >= song.songLength)
1638 {
1639 song.songPos = song.songLength - 1;
1640 setPos(song.songPos, -1, false);
1641 }
1642
1643 ui.updatePosSections = true;
1644 ui.updatePosEdScrollBar = true;
1645 setSongModifiedFlag();
1646
1647 if (audioWasntLocked)
1648 unlockAudio();
1649 }
1650
pbPosEdRepSUp(void)1651 void pbPosEdRepSUp(void)
1652 {
1653 const bool audioWasntLocked = !audio.locked;
1654 if (audioWasntLocked)
1655 lockAudio();
1656
1657 if (song.songLoopStart < song.songLength-1)
1658 {
1659 song.songLoopStart++;
1660 ui.updatePosSections = true;
1661 setSongModifiedFlag();
1662 }
1663
1664 if (audioWasntLocked)
1665 unlockAudio();
1666 }
1667
pbPosEdRepSDown(void)1668 void pbPosEdRepSDown(void)
1669 {
1670 const bool audioWasntLocked = !audio.locked;
1671 if (audioWasntLocked)
1672 lockAudio();
1673
1674 if (song.songLoopStart > 0)
1675 {
1676 song.songLoopStart--;
1677 ui.updatePosSections = true;
1678 setSongModifiedFlag();
1679 }
1680
1681 if (audioWasntLocked)
1682 unlockAudio();
1683 }
1684
pbBPMUp(void)1685 void pbBPMUp(void)
1686 {
1687 if (song.BPM == 255)
1688 return;
1689
1690 const bool audioWasntLocked = !audio.locked;
1691 if (audioWasntLocked)
1692 lockAudio();
1693
1694 if (song.BPM < 255)
1695 {
1696 song.BPM++;
1697 setMixerBPM(song.BPM);
1698
1699 // if song is playing, the update is handled in the audio/video sync queue
1700 if (!songPlaying)
1701 {
1702 editor.BPM = song.BPM;
1703 drawSongBPM(song.BPM);
1704 }
1705 }
1706
1707 if (audioWasntLocked)
1708 unlockAudio();
1709 }
1710
pbBPMDown(void)1711 void pbBPMDown(void)
1712 {
1713 if (song.BPM == 32)
1714 return;
1715
1716 const bool audioWasntLocked = !audio.locked;
1717 if (audioWasntLocked)
1718 lockAudio();
1719
1720 if (song.BPM > 32)
1721 {
1722 song.BPM--;
1723 setMixerBPM(song.BPM);
1724
1725 // if song is playing, the update is handled in the audio/video sync queue
1726 if (!songPlaying)
1727 {
1728 editor.BPM = song.BPM;
1729 drawSongBPM(editor.BPM);
1730 }
1731 }
1732
1733 if (audioWasntLocked)
1734 unlockAudio();
1735 }
1736
pbSpeedUp(void)1737 void pbSpeedUp(void)
1738 {
1739 if (song.speed == 31)
1740 return;
1741
1742 const bool audioWasntLocked = !audio.locked;
1743 if (audioWasntLocked)
1744 lockAudio();
1745
1746 if (song.speed < 31)
1747 {
1748 song.speed++;
1749
1750 // if song is playing, the update is handled in the audio/video sync queue
1751 if (!songPlaying)
1752 {
1753 editor.speed = song.speed;
1754 drawSongSpeed(editor.speed);
1755 }
1756 }
1757
1758 if (audioWasntLocked)
1759 unlockAudio();
1760 }
1761
pbSpeedDown(void)1762 void pbSpeedDown(void)
1763 {
1764 if (song.speed == 0)
1765 return;
1766
1767 const bool audioWasntLocked = !audio.locked;
1768 if (audioWasntLocked)
1769 lockAudio();
1770
1771 if (song.speed > 0)
1772 {
1773 song.speed--;
1774
1775 // if song is playing, the update is handled in the audio/video sync queue
1776 if (!songPlaying)
1777 {
1778 editor.speed = song.speed;
1779 drawSongSpeed(editor.speed);
1780 }
1781 }
1782
1783 if (audioWasntLocked)
1784 unlockAudio();
1785 }
1786
pbIncAdd(void)1787 void pbIncAdd(void)
1788 {
1789 if (editor.editRowSkip == 16)
1790 editor.editRowSkip = 0;
1791 else
1792 editor.editRowSkip++;
1793
1794 drawIDAdd();
1795 }
1796
pbDecAdd(void)1797 void pbDecAdd(void)
1798 {
1799 if (editor.editRowSkip == 0)
1800 editor.editRowSkip = 16;
1801 else
1802 editor.editRowSkip--;
1803
1804 drawIDAdd();
1805 }
1806
pbAddChan(void)1807 void pbAddChan(void)
1808 {
1809 if (song.numChannels > 30)
1810 return;
1811
1812 lockMixerCallback();
1813 song.numChannels += 2;
1814
1815 hideTopScreen();
1816 showTopLeftMainScreen(true);
1817 showTopRightMainScreen();
1818
1819 if (ui.patternEditorShown)
1820 showPatternEditor();
1821
1822 setSongModifiedFlag();
1823 unlockMixerCallback();
1824 }
1825
pbSubChan(void)1826 void pbSubChan(void)
1827 {
1828 if (song.numChannels < 4)
1829 return;
1830
1831 lockMixerCallback();
1832
1833 song.numChannels -= 2;
1834
1835 checkMarkLimits();
1836
1837 hideTopScreen();
1838 showTopLeftMainScreen(true);
1839 showTopRightMainScreen();
1840
1841 if (ui.patternEditorShown)
1842 showPatternEditor();
1843
1844 setSongModifiedFlag();
1845 unlockMixerCallback();
1846 }
1847
pbEditPattUp(void)1848 void pbEditPattUp(void)
1849 {
1850 const bool audioWasntLocked = !audio.locked;
1851 if (audioWasntLocked)
1852 lockAudio();
1853
1854 if (song.pattNum < 255)
1855 {
1856 song.pattNum++;
1857
1858 song.currNumRows = patternNumRows[song.pattNum];
1859 if (song.row >= song.currNumRows)
1860 {
1861 song.row = song.currNumRows-1;
1862 if (!songPlaying)
1863 editor.row = song.row;
1864 }
1865
1866 if (!songPlaying)
1867 editor.editPattern = (uint8_t)song.pattNum;
1868
1869 checkMarkLimits();
1870 ui.updatePatternEditor = true;
1871 ui.updatePosSections = true;
1872 }
1873
1874 if (audioWasntLocked)
1875 unlockAudio();
1876 }
1877
pbEditPattDown(void)1878 void pbEditPattDown(void)
1879 {
1880 const bool audioWasntLocked = !audio.locked;
1881 if (audioWasntLocked)
1882 lockAudio();
1883
1884 if (song.pattNum > 0)
1885 {
1886 song.pattNum--;
1887
1888 song.currNumRows = patternNumRows[song.pattNum];
1889 if (song.row >= song.currNumRows)
1890 {
1891 song.row = song.currNumRows-1;
1892 if (!songPlaying)
1893 editor.row = song.row;
1894 }
1895
1896 if (!songPlaying)
1897 editor.editPattern = (uint8_t)song.pattNum;
1898
1899 checkMarkLimits();
1900 ui.updatePatternEditor = true;
1901 ui.updatePosSections = true;
1902 }
1903
1904 if (audioWasntLocked)
1905 unlockAudio();
1906 }
1907
pbPattLenUp(void)1908 void pbPattLenUp(void)
1909 {
1910 const uint16_t numRows = patternNumRows[editor.editPattern];
1911 if (numRows >= MAX_PATT_LEN)
1912 return;
1913
1914 const bool audioWasntLocked = !audio.locked;
1915 if (audioWasntLocked)
1916 lockAudio();
1917
1918 song.pattNum = editor.editPattern; // kludge
1919 setPatternLen(editor.editPattern, numRows+1);
1920
1921 ui.updatePatternEditor = true;
1922 ui.updatePosSections = true;
1923 setSongModifiedFlag();
1924
1925 if (audioWasntLocked)
1926 unlockAudio();
1927 }
1928
pbPattLenDown(void)1929 void pbPattLenDown(void)
1930 {
1931 const uint16_t numRows = patternNumRows[editor.editPattern];
1932 if (numRows <= 1)
1933 return;
1934
1935 const bool audioWasntLocked = !audio.locked;
1936 if (audioWasntLocked)
1937 lockAudio();
1938
1939 song.pattNum = editor.editPattern; // kludge
1940 setPatternLen(editor.editPattern, numRows-1);
1941
1942 ui.updatePatternEditor = true;
1943 ui.updatePosSections = true;
1944 setSongModifiedFlag();
1945
1946 if (audioWasntLocked)
1947 unlockAudio();
1948 }
1949
drawPosEdNums(int16_t songPos)1950 void drawPosEdNums(int16_t songPos)
1951 {
1952 if (songPos >= song.songLength)
1953 songPos = song.songLength - 1;
1954
1955 // clear
1956 if (ui.extended)
1957 {
1958 clearRect(8, 4, 39, 16);
1959 fillRect(8, 23, 39, 7, PAL_DESKTOP);
1960 clearRect(8, 33, 39, 16);
1961 }
1962 else
1963 {
1964 clearRect(8, 4, 39, 15);
1965 fillRect(8, 22, 39, 7, PAL_DESKTOP);
1966 clearRect(8, 32, 39, 15);
1967 }
1968
1969 const uint32_t color1 = video.palette[PAL_PATTEXT];
1970 const uint32_t color2 = video.palette[PAL_FORGRND];
1971
1972 // top two
1973 for (int16_t y = 0; y < 2; y++)
1974 {
1975 int16_t entry = songPos - (2 - y);
1976 if (entry < 0)
1977 continue;
1978
1979 assert(entry < 256);
1980
1981 if (ui.extended)
1982 {
1983 pattTwoHexOut(8, 4 + (y * 9), (uint8_t)entry, color1);
1984 pattTwoHexOut(32, 4 + (y * 9), song.orders[entry], color1);
1985 }
1986 else
1987 {
1988 pattTwoHexOut(8, 4 + (y * 8), (uint8_t)entry, color1);
1989 pattTwoHexOut(32, 4 + (y * 8), song.orders[entry], color1);
1990 }
1991 }
1992
1993 assert(songPos < 256);
1994
1995 // middle
1996 if (ui.extended)
1997 {
1998 pattTwoHexOut(8, 23, (uint8_t)songPos, color2);
1999 pattTwoHexOut(32, 23, song.orders[songPos], color2);
2000 }
2001 else
2002 {
2003 pattTwoHexOut(8, 22, (uint8_t)songPos, color2);
2004 pattTwoHexOut(32, 22, song.orders[songPos], color2);
2005 }
2006
2007 // bottom two
2008 for (int16_t y = 0; y < 2; y++)
2009 {
2010 int16_t entry = songPos + (1 + y);
2011 if (entry >= song.songLength)
2012 break;
2013
2014 if (ui.extended)
2015 {
2016 pattTwoHexOut(8, 33 + (y * 9), (uint8_t)entry, color1);
2017 pattTwoHexOut(32, 33 + (y * 9), song.orders[entry], color1);
2018 }
2019 else
2020 {
2021 pattTwoHexOut(8, 32 + (y * 8), (uint8_t)entry, color1);
2022 pattTwoHexOut(32, 32 + (y * 8), song.orders[entry], color1);
2023 }
2024 }
2025 }
2026
drawSongLength(void)2027 void drawSongLength(void)
2028 {
2029 int16_t x, y;
2030
2031 if (ui.extended)
2032 {
2033 x = 165;
2034 y = 5;
2035 }
2036 else
2037 {
2038 x = 59;
2039 y = 52;
2040 }
2041
2042 hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLength, 2);
2043 }
2044
drawSongLoopStart(void)2045 void drawSongLoopStart(void)
2046 {
2047 int16_t x, y;
2048
2049 if (ui.extended)
2050 {
2051 x = 165;
2052 y = 19;
2053 }
2054 else
2055 {
2056 x = 59;
2057 y = 64;
2058 }
2059
2060 hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLoopStart, 2);
2061 }
2062
drawSongBPM(uint16_t val)2063 void drawSongBPM(uint16_t val)
2064 {
2065 if (ui.extended)
2066 return;
2067
2068 if (val > 255)
2069 val = 255;
2070
2071 textOutFixed(145, 36, PAL_FORGRND, PAL_DESKTOP, dec3StrTab[val]);
2072 }
2073
drawSongSpeed(uint16_t val)2074 void drawSongSpeed(uint16_t val)
2075 {
2076 if (ui.extended)
2077 return;
2078
2079 if (val > 99)
2080 val = 99;
2081
2082 textOutFixed(152, 50, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[val]);
2083 }
2084
drawEditPattern(uint16_t editPattern)2085 void drawEditPattern(uint16_t editPattern)
2086 {
2087 int16_t x, y;
2088
2089 if (ui.extended)
2090 {
2091 x = 252;
2092 y = 39;
2093 }
2094 else
2095 {
2096 x = 237;
2097 y = 36;
2098 }
2099
2100 hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, editPattern, 2);
2101 }
2102
drawPatternLength(uint16_t editPattern)2103 void drawPatternLength(uint16_t editPattern)
2104 {
2105 int16_t x, y;
2106
2107 if (ui.extended)
2108 {
2109 x = 326;
2110 y = 39;
2111 }
2112 else
2113 {
2114 x = 230;
2115 y = 50;
2116 }
2117
2118 hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, patternNumRows[editPattern], 3);
2119 }
2120
drawGlobalVol(uint16_t val)2121 void drawGlobalVol(uint16_t val)
2122 {
2123 if (ui.extended)
2124 return;
2125
2126 assert(val <= 64);
2127 textOutFixed(87, 80, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[val]);
2128 }
2129
drawIDAdd(void)2130 void drawIDAdd(void)
2131 {
2132 assert(editor.editRowSkip <= 16);
2133 textOutFixed(152, 64, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[editor.editRowSkip]);
2134 }
2135
resetPlaybackTime(void)2136 void resetPlaybackTime(void)
2137 {
2138 song.musicTime64 = 0;
2139 last_TimeH = 0;
2140 last_TimeM = 0;
2141 last_TimeS = 0;
2142 }
2143
drawPlaybackTime(void)2144 void drawPlaybackTime(void)
2145 {
2146 if (songPlaying)
2147 {
2148 const uint32_t ms1024 = song.musicTime64 >> 32; // milliseconds (scaled from 1000 to 1024)
2149 uint32_t seconds = ms1024 >> 10;
2150
2151 last_TimeH = seconds / 3600;
2152 seconds -= last_TimeH * 3600;
2153
2154 last_TimeM = seconds / 60;
2155 seconds -= last_TimeM * 60;
2156
2157 last_TimeS = seconds;
2158 }
2159
2160 textOutFixed(235, 80, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[last_TimeH]);
2161 textOutFixed(255, 80, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[last_TimeM]);
2162 textOutFixed(275, 80, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[last_TimeS]);
2163 }
2164
drawSongName(void)2165 void drawSongName(void)
2166 {
2167 drawFramework(421, 155, 166, 18, FRAMEWORK_TYPE1);
2168 drawFramework(423, 157, 162, 14, FRAMEWORK_TYPE2);
2169 drawTextBox(TB_SONG_NAME);
2170 }
2171
changeLogoType(uint8_t logoType)2172 void changeLogoType(uint8_t logoType)
2173 {
2174 pushButtons[PB_LOGO].bitmapFlag = true;
2175
2176 if (logoType == 0)
2177 {
2178 pushButtons[PB_LOGO].bitmapUnpressed = &bmp.ft2LogoBadges[(154 * 32) * 0];
2179 pushButtons[PB_LOGO].bitmapPressed = &bmp.ft2LogoBadges[(154 * 32) * 1];
2180 }
2181 else
2182 {
2183 pushButtons[PB_LOGO].bitmapUnpressed = &bmp.ft2LogoBadges[(154 * 32) * 2];
2184 pushButtons[PB_LOGO].bitmapPressed = &bmp.ft2LogoBadges[(154 * 32) * 3];
2185 }
2186
2187 drawPushButton(PB_LOGO);
2188 }
2189
changeBadgeType(uint8_t badgeType)2190 void changeBadgeType(uint8_t badgeType)
2191 {
2192 pushButtons[PB_BADGE].bitmapFlag = true;
2193
2194 if (badgeType == 0)
2195 {
2196 pushButtons[PB_BADGE].bitmapUnpressed = &bmp.ft2ByBadges[(25 * 32) * 0];
2197 pushButtons[PB_BADGE].bitmapPressed = &bmp.ft2ByBadges[(25 * 32) * 1];
2198 }
2199 else
2200 {
2201 pushButtons[PB_BADGE].bitmapUnpressed = &bmp.ft2ByBadges[(25 * 32) * 2];
2202 pushButtons[PB_BADGE].bitmapPressed = &bmp.ft2ByBadges[(25 * 32) * 3];
2203 }
2204
2205 drawPushButton(PB_BADGE);
2206 }
2207
updateInstrumentSwitcher(void)2208 void updateInstrumentSwitcher(void)
2209 {
2210 int16_t y;
2211
2212 if (ui.aboutScreenShown || ui.configScreenShown || ui.helpScreenShown || ui.nibblesShown)
2213 return; // don't redraw instrument switcher when it's not shown!
2214
2215 if (ui.extended) // extended pattern editor
2216 {
2217 //INSTRUMENTS
2218
2219 clearRect(388, 5, 116, 43); // left box
2220 clearRect(511, 5, 116, 43); // right box
2221
2222 // draw source instrument selection
2223 if (editor.srcInstr >= editor.instrBankOffset && editor.srcInstr <= editor.instrBankOffset+8)
2224 {
2225 y = 5 + ((editor.srcInstr - editor.instrBankOffset - 1) * 11);
2226 if (y >= 5 && y <= 82)
2227 {
2228 if (y <= 47)
2229 fillRect(388, y, 15, 10, PAL_BUTTONS); // left box
2230 else
2231 fillRect(511, y - 44, 15, 10, PAL_BUTTONS); // right box
2232 }
2233 }
2234
2235 // draw destination instrument selection
2236 if (editor.curInstr >= editor.instrBankOffset && editor.curInstr <= editor.instrBankOffset+8)
2237 {
2238 y = 5 + ((editor.curInstr - editor.instrBankOffset - 1) * 11);
2239 y = 5 + ((editor.curInstr - editor.instrBankOffset - 1) * 11);
2240 if (y >= 5 && y <= 82)
2241 {
2242 if (y <= 47)
2243 fillRect(406, y, 98, 10, PAL_BUTTONS); // left box
2244 else
2245 fillRect(529, y - 44, 98, 10, PAL_BUTTONS); // right box
2246 }
2247 }
2248
2249 // draw numbers and texts
2250 for (int16_t i = 0; i < 4; i++)
2251 {
2252 hexOut(388, 5 + (i * 11), PAL_FORGRND, 1 + editor.instrBankOffset + i, 2);
2253 hexOut(511, 5 + (i * 11), PAL_FORGRND, 5 + editor.instrBankOffset + i, 2);
2254 drawTextBox(TB_INST1 + i);
2255 drawTextBox(TB_INST5 + i);
2256 }
2257 }
2258 else // normal pattern editor
2259 {
2260 // INSTRUMENTS
2261
2262 clearRect(424, 5, 15, 87); // src instrument
2263 clearRect(446, 5, 139, 87); // main instrument
2264
2265 // draw source instrument selection
2266 if (editor.srcInstr >= editor.instrBankOffset && editor.srcInstr <= editor.instrBankOffset+8)
2267 {
2268 y = 5 + ((editor.srcInstr - editor.instrBankOffset - 1) * 11);
2269 if (y >= 5 && y <= 82)
2270 fillRect(424, y, 15, 10, PAL_BUTTONS);
2271 }
2272
2273 // draw destination instrument selection
2274 if (editor.curInstr >= editor.instrBankOffset && editor.curInstr <= editor.instrBankOffset+8)
2275 {
2276 y = 5 + ((editor.curInstr - editor.instrBankOffset - 1) * 11);
2277 if (y >= 5 && y <= 82)
2278 fillRect(446, y, 139, 10, PAL_BUTTONS);
2279 }
2280
2281 // draw numbers and texts
2282 for (int16_t i = 0; i < 8; i++)
2283 {
2284 hexOut(424, 5 + (i * 11), PAL_FORGRND, 1 + editor.instrBankOffset + i, 2);
2285 drawTextBox(TB_INST1 + i);
2286 }
2287
2288 // SAMPLES
2289
2290 clearRect(424, 99, 15, 54); // src sample
2291 clearRect(446, 99, 115, 54); // main sample
2292
2293 // draw source sample selection
2294 if (editor.srcSmp >= editor.sampleBankOffset && editor.srcSmp <= editor.sampleBankOffset+4)
2295 {
2296 y = 99 + ((editor.srcSmp - editor.sampleBankOffset) * 11);
2297 if (y >= 36 && y <= 143)
2298 fillRect(424, y, 15, 10, PAL_BUTTONS);
2299 }
2300
2301 // draw destination sample selection
2302 if (editor.curSmp >= editor.sampleBankOffset && editor.curSmp <= editor.sampleBankOffset+4)
2303 {
2304 y = 99 + ((editor.curSmp - editor.sampleBankOffset) * 11);
2305 if (y >= 36 && y <= 143)
2306 fillRect(446, y, 115, 10, PAL_BUTTONS);
2307 }
2308
2309 // draw numbers and texts
2310 for (int16_t i = 0; i < 5; i++)
2311 {
2312 hexOut(424, 99 + (i * 11), PAL_FORGRND, editor.sampleBankOffset + i, 2);
2313 drawTextBox(TB_SAMP1 + i);
2314 }
2315 }
2316 }
2317
showInstrumentSwitcher(void)2318 void showInstrumentSwitcher(void)
2319 {
2320 if (!ui.instrSwitcherShown)
2321 return;
2322
2323 for (uint16_t i = 0; i < 8; i++)
2324 showTextBox(TB_INST1 + i);
2325
2326 if (ui.extended)
2327 {
2328 hidePushButton(PB_SAMPLE_LIST_UP);
2329 hidePushButton(PB_SAMPLE_LIST_DOWN);
2330 hideScrollBar(SB_SAMPLE_LIST);
2331
2332 drawFramework(386, 0, 246, 3, FRAMEWORK_TYPE1);
2333 drawFramework(506, 3, 3, 47, FRAMEWORK_TYPE1);
2334 drawFramework(386, 50, 246, 3, FRAMEWORK_TYPE1);
2335 drawFramework(629, 3, 3, 47, FRAMEWORK_TYPE1);
2336
2337 clearRect(386, 3, 120, 47);
2338 clearRect(509, 3, 120, 47);
2339 }
2340 else
2341 {
2342 drawFramework(421, 0, 166, 3, FRAMEWORK_TYPE1);
2343 drawFramework(442, 3, 3, 91, FRAMEWORK_TYPE1);
2344 drawFramework(421, 94, 166, 3, FRAMEWORK_TYPE1);
2345 drawFramework(442, 97, 3, 58, FRAMEWORK_TYPE1);
2346 drawFramework(563, 97, 24, 58, FRAMEWORK_TYPE1);
2347 drawFramework(587, 0, 45, 71, FRAMEWORK_TYPE1);
2348 drawFramework(587, 71, 45, 71, FRAMEWORK_TYPE1);
2349 drawFramework(587, 142, 45, 31, FRAMEWORK_TYPE1);
2350
2351 fillRect(421, 3, 21, 91, PAL_BCKGRND);
2352 fillRect(445, 3, 142, 91, PAL_BCKGRND);
2353 fillRect(421, 97, 21, 58, PAL_BCKGRND);
2354 fillRect(445, 97, 118, 58, PAL_BCKGRND);
2355
2356 showPushButton(PB_SAMPLE_LIST_UP);
2357 showPushButton(PB_SAMPLE_LIST_DOWN);
2358 showScrollBar(SB_SAMPLE_LIST);
2359
2360 for (uint16_t i = 0; i < 5; i++)
2361 showTextBox(TB_SAMP1 + i);
2362 }
2363
2364 updateInstrumentSwitcher();
2365
2366 for (uint16_t i = 0; i < 8; i++)
2367 showPushButton(PB_RANGE1 + i + (editor.instrBankSwapped * 8));
2368
2369 showPushButton(PB_SWAP_BANK);
2370 }
2371
hideInstrumentSwitcher(void)2372 void hideInstrumentSwitcher(void)
2373 {
2374 for (uint16_t i = 0; i < 16; i++)
2375 hidePushButton(PB_RANGE1 + i);
2376
2377 hidePushButton(PB_SWAP_BANK);
2378 hidePushButton(PB_SAMPLE_LIST_UP);
2379 hidePushButton(PB_SAMPLE_LIST_DOWN);
2380 hideScrollBar(SB_SAMPLE_LIST);
2381
2382 for (uint16_t i = 0; i < 8; i++)
2383 hideTextBox(TB_INST1 + i);
2384
2385 for (uint16_t i = 0; i < 5; i++)
2386 hideTextBox(TB_SAMP1 + i);
2387 }
2388
pbSwapInstrBank(void)2389 void pbSwapInstrBank(void)
2390 {
2391 editor.instrBankSwapped ^= 1;
2392
2393 if (editor.instrBankSwapped)
2394 editor.instrBankOffset += 8*8;
2395 else
2396 editor.instrBankOffset -= 8*8;
2397
2398 updateTextBoxPointers();
2399
2400 if (ui.instrSwitcherShown)
2401 {
2402 updateInstrumentSwitcher();
2403 for (uint16_t i = 0; i < 8; i++)
2404 {
2405 hidePushButton(PB_RANGE1 + i + (!editor.instrBankSwapped * 8));
2406 showPushButton(PB_RANGE1 + i + ( editor.instrBankSwapped * 8));
2407 }
2408 }
2409 }
2410
pbSetInstrBank1(void)2411 void pbSetInstrBank1(void)
2412 {
2413 editor.instrBankOffset = 0 * 8;
2414
2415 updateTextBoxPointers();
2416 updateInstrumentSwitcher();
2417 }
2418
pbSetInstrBank2(void)2419 void pbSetInstrBank2(void)
2420 {
2421 editor.instrBankOffset = 1 * 8;
2422
2423 updateTextBoxPointers();
2424 updateInstrumentSwitcher();
2425 }
2426
pbSetInstrBank3(void)2427 void pbSetInstrBank3(void)
2428 {
2429 editor.instrBankOffset = 2 * 8;
2430
2431 updateTextBoxPointers();
2432 updateInstrumentSwitcher();
2433 }
2434
pbSetInstrBank4(void)2435 void pbSetInstrBank4(void)
2436 {
2437 editor.instrBankOffset = 3 * 8;
2438
2439 updateTextBoxPointers();
2440 updateInstrumentSwitcher();
2441 }
2442
pbSetInstrBank5(void)2443 void pbSetInstrBank5(void)
2444 {
2445 editor.instrBankOffset = 4 * 8;
2446
2447 updateTextBoxPointers();
2448 updateInstrumentSwitcher();
2449 }
2450
pbSetInstrBank6(void)2451 void pbSetInstrBank6(void)
2452 {
2453 editor.instrBankOffset = 5 * 8;
2454
2455 updateTextBoxPointers();
2456 updateInstrumentSwitcher();
2457 }
2458
pbSetInstrBank7(void)2459 void pbSetInstrBank7(void)
2460 {
2461 editor.instrBankOffset = 6 * 8;
2462
2463 updateTextBoxPointers();
2464 updateInstrumentSwitcher();
2465 }
2466
pbSetInstrBank8(void)2467 void pbSetInstrBank8(void)
2468 {
2469 editor.instrBankOffset = 7 * 8;
2470
2471 updateTextBoxPointers();
2472 updateInstrumentSwitcher();
2473 }
2474
pbSetInstrBank9(void)2475 void pbSetInstrBank9(void)
2476 {
2477 editor.instrBankOffset = 8 * 8;
2478
2479 updateTextBoxPointers();
2480 updateInstrumentSwitcher();
2481 }
2482
pbSetInstrBank10(void)2483 void pbSetInstrBank10(void)
2484 {
2485 editor.instrBankOffset = 9 * 8;
2486
2487 updateTextBoxPointers();
2488 updateInstrumentSwitcher();
2489 }
2490
pbSetInstrBank11(void)2491 void pbSetInstrBank11(void)
2492 {
2493 editor.instrBankOffset = 10 * 8;
2494
2495 updateTextBoxPointers();
2496 updateInstrumentSwitcher();
2497 }
2498
pbSetInstrBank12(void)2499 void pbSetInstrBank12(void)
2500 {
2501 editor.instrBankOffset = 11 * 8;
2502
2503 updateTextBoxPointers();
2504 updateInstrumentSwitcher();
2505 }
2506
pbSetInstrBank13(void)2507 void pbSetInstrBank13(void)
2508 {
2509 editor.instrBankOffset = 12 * 8;
2510
2511 updateTextBoxPointers();
2512 updateInstrumentSwitcher();
2513 }
2514
pbSetInstrBank14(void)2515 void pbSetInstrBank14(void)
2516 {
2517 editor.instrBankOffset = 13 * 8;
2518
2519 updateTextBoxPointers();
2520 updateInstrumentSwitcher();
2521 }
2522
pbSetInstrBank15(void)2523 void pbSetInstrBank15(void)
2524 {
2525 editor.instrBankOffset = 14 * 8;
2526
2527 updateTextBoxPointers();
2528 updateInstrumentSwitcher();
2529 }
2530
pbSetInstrBank16(void)2531 void pbSetInstrBank16(void)
2532 {
2533 editor.instrBankOffset = 15 * 8;
2534
2535 updateTextBoxPointers();
2536 updateInstrumentSwitcher();
2537 }
2538
setNewInstr(int16_t ins)2539 void setNewInstr(int16_t ins)
2540 {
2541 if (ins <= MAX_INST)
2542 {
2543 editor.curInstr = (uint8_t)ins;
2544 updateTextBoxPointers();
2545 updateInstrumentSwitcher();
2546 updateNewInstrument();
2547 }
2548 }
2549
sampleListScrollUp(void)2550 void sampleListScrollUp(void)
2551 {
2552 scrollBarScrollUp(SB_SAMPLE_LIST, 1);
2553 }
2554
sampleListScrollDown(void)2555 void sampleListScrollDown(void)
2556 {
2557 scrollBarScrollDown(SB_SAMPLE_LIST, 1);
2558 }
2559
zapSong(void)2560 static void zapSong(void)
2561 {
2562 lockMixerCallback();
2563
2564 song.songLength = 1;
2565 song.songLoopStart = 0; // FT2 doesn't do this!
2566 song.BPM = 125;
2567 song.speed = 6;
2568 song.songPos = 0;
2569 song.globalVolume = 64;
2570
2571 memset(song.name, 0, sizeof (song.name));
2572 memset(song.orders, 0, sizeof (song.orders));
2573
2574 // zero all pattern data and reset pattern lengths
2575
2576 freeAllPatterns();
2577 for (int32_t i = 0; i < MAX_PATTERNS; i++)
2578 patternNumRows[i] = 64;
2579 song.currNumRows = patternNumRows[song.pattNum];
2580
2581 resetMusic();
2582 setMixerBPM(song.BPM);
2583
2584 editor.songPos = song.songPos;
2585 editor.editPattern = song.pattNum;
2586 editor.BPM = song.BPM;
2587 editor.speed = song.speed;
2588 editor.globalVolume = song.globalVolume;
2589 editor.tick = 1;
2590
2591 resetPlaybackTime();
2592
2593 if (!audio.linearPeriodsFlag)
2594 setFrequencyTable(true);
2595
2596 clearPattMark();
2597 resetWavRenderer();
2598 resetChannels();
2599 unlockMixerCallback();
2600
2601 setScrollBarPos(SB_POS_ED, 0, false);
2602 setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);
2603
2604 updateWindowTitle(true);
2605 }
2606
zapInstrs(void)2607 static void zapInstrs(void)
2608 {
2609 lockMixerCallback();
2610
2611 for (int16_t i = 1; i <= MAX_INST; i++)
2612 {
2613 freeInstr(i);
2614 memset(song.instrName[i], 0, 22+1);
2615 }
2616
2617 updateNewInstrument();
2618
2619 editor.currVolEnvPoint = 0;
2620 editor.currPanEnvPoint = 0;
2621
2622 updateSampleEditorSample();
2623
2624 if (ui.sampleEditorShown)
2625 updateSampleEditor();
2626 else if (ui.instEditorShown || ui.instEditorExtShown)
2627 updateInstEditor();
2628
2629 unlockMixerCallback();
2630 }
2631
pbZap(void)2632 void pbZap(void)
2633 {
2634 const int16_t choice = okBox(4, "System request", "Total devastation of the...");
2635
2636 if (choice == 1) // zap all
2637 {
2638 zapSong();
2639 zapInstrs();
2640 }
2641 else if (choice == 2) // zap song
2642 {
2643 zapSong();
2644 }
2645 else if (choice == 3) // zap instruments
2646 {
2647 zapInstrs();
2648 }
2649
2650 if (choice >= 1 && choice <= 3)
2651 {
2652 // redraw top screens
2653 hideTopScreen();
2654 showTopScreen(true);
2655
2656 setSongModifiedFlag();
2657 }
2658 }
2659
sbSmpBankPos(uint32_t pos)2660 void sbSmpBankPos(uint32_t pos)
2661 {
2662 if (editor.sampleBankOffset != pos)
2663 {
2664 editor.sampleBankOffset = (uint8_t)pos;
2665
2666 updateTextBoxPointers();
2667 updateInstrumentSwitcher();
2668 }
2669 }
2670
pbToggleLogo(void)2671 void pbToggleLogo(void)
2672 {
2673 config.id_FastLogo ^= 1;
2674 changeLogoType(config.id_FastLogo);
2675 }
2676
pbToggleBadge(void)2677 void pbToggleBadge(void)
2678 {
2679 config.id_TritonProd ^= 1;
2680 changeBadgeType(config.id_TritonProd);
2681 }
2682
resetChannelOffset(void)2683 void resetChannelOffset(void)
2684 {
2685 ui.pattChanScrollShown = song.numChannels > getMaxVisibleChannels();
2686 cursor.object = CURSOR_NOTE;
2687 cursor.ch = 0;
2688 setScrollBarPos(SB_CHAN_SCROLL, 0, true);
2689 ui.channelOffset = 0;
2690 }
2691
shrinkPattern(void)2692 void shrinkPattern(void)
2693 {
2694 if (okBox(2, "System request", "Shrink pattern?") != 1)
2695 return;
2696
2697 int16_t numRows = patternNumRows[editor.editPattern];
2698 if (numRows <= 1)
2699 return;
2700
2701 lockMixerCallback();
2702
2703 note_t *p = pattern[editor.editPattern];
2704 if (p != NULL)
2705 {
2706 const int32_t length = numRows >> 1;
2707 for (int32_t i = 0; i < length; i++)
2708 {
2709 for (int32_t j = 0; j < MAX_CHANNELS; j++)
2710 p[(i * MAX_CHANNELS) + j] = p[((i*2) * MAX_CHANNELS) + j];
2711 }
2712 }
2713
2714 patternNumRows[editor.editPattern] >>= 1;
2715 numRows = patternNumRows[editor.editPattern];
2716
2717 if (song.pattNum == editor.editPattern)
2718 song.currNumRows = numRows;
2719
2720 song.row >>= 1;
2721 if (song.row >= numRows)
2722 song.row = numRows-1;
2723
2724 editor.row = song.row;
2725
2726 ui.updatePatternEditor = true;
2727 ui.updatePosSections = true;
2728
2729 unlockMixerCallback();
2730 setSongModifiedFlag();
2731 }
2732
expandPattern(void)2733 void expandPattern(void)
2734 {
2735 int16_t numRows = patternNumRows[editor.editPattern];
2736 if (numRows > 128)
2737 {
2738 okBox(0, "System message", "Pattern is too long to be expanded.");
2739 }
2740 else
2741 {
2742 lockMixerCallback();
2743
2744 if (pattern[editor.editPattern] != NULL)
2745 {
2746 note_t *tmpPtn = (note_t *)malloc((numRows * 2) * TRACK_WIDTH);
2747 if (tmpPtn == NULL)
2748 {
2749 unlockMixerCallback();
2750 okBox(0, "System message", "Not enough memory!");
2751 return;
2752 }
2753
2754 for (int32_t i = 0; i < numRows; i++)
2755 {
2756 for (int32_t j = 0; j < MAX_CHANNELS; j++)
2757 tmpPtn[((i * 2) * MAX_CHANNELS) + j] = pattern[editor.editPattern][(i * MAX_CHANNELS) + j];
2758
2759 memset(&tmpPtn[((i * 2) + 1) * MAX_CHANNELS], 0, TRACK_WIDTH);
2760 }
2761
2762 free(pattern[editor.editPattern]);
2763 pattern[editor.editPattern] = tmpPtn;
2764 }
2765
2766 patternNumRows[editor.editPattern] *= 2;
2767 numRows = patternNumRows[editor.editPattern];
2768
2769 if (song.pattNum == editor.editPattern)
2770 song.currNumRows = numRows;
2771
2772 song.row *= 2;
2773 if (song.row >= numRows)
2774 song.row = numRows-1;
2775
2776 editor.row = song.row;
2777
2778 ui.updatePatternEditor = true;
2779 ui.updatePosSections = true;
2780
2781 unlockMixerCallback();
2782 setSongModifiedFlag();
2783 }
2784 }
2785