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