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 "ft2_header.h"
9 #include "ft2_config.h"
10 #include "ft2_keyboard.h"
11 #include "ft2_audio.h"
12 #include "ft2_midi.h"
13 #include "ft2_pattern_ed.h"
14 #include "ft2_sysreqs.h"
15 #include "ft2_textboxes.h"
16 #include "ft2_tables.h"
17 #include "ft2_structs.h"
18
19 enum
20 {
21 KEYTYPE_NUM = 0,
22 KEYTYPE_ALPHA = 1
23 };
24
25 static double dVolScaleFK1 = 1.0, dVolScaleFK2 = 1.0;
26
27 // for block cut/copy/paste
28 static bool blockCopied;
29 static int16_t markXSize, markYSize;
30 static uint16_t ptnBufLen, trkBufLen;
31
32 // for transposing - these are set and tested accordingly
33 static int8_t lastTranspVal;
34 static uint8_t lastInsMode, lastTranspMode;
35 static uint32_t transpDelNotes; // count of under-/overflowing notes for warning message
36 static note_t clearNote;
37
38 static note_t blkCopyBuff[MAX_PATT_LEN * MAX_CHANNELS];
39 static note_t ptnCopyBuff[MAX_PATT_LEN * MAX_CHANNELS];
40 static note_t trackCopyBuff[MAX_PATT_LEN];
41
42 static const int8_t tickArr[16] = { 16, 8, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1 };
43
44 void recordNote(uint8_t note, int8_t vol);
45
46 // when the cursor is at the note slot
testNoteKeys(SDL_Scancode scancode)47 static bool testNoteKeys(SDL_Scancode scancode)
48 {
49 const int8_t noteNum = scancodeKeyToNote(scancode);
50 if (noteNum == NOTE_OFF)
51 {
52 // inserts "note off" if editing song
53 if (playMode == PLAYMODE_EDIT || playMode == PLAYMODE_RECPATT || playMode == PLAYMODE_RECSONG)
54 {
55 if (!allocatePattern(editor.editPattern))
56 return true; // key pressed
57
58 pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch].note = NOTE_OFF;
59
60 const uint16_t numRows = patternNumRows[editor.editPattern];
61 if (playMode == PLAYMODE_EDIT && numRows >= 1)
62 setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
63
64 ui.updatePatternEditor = true;
65 setSongModifiedFlag();
66 }
67
68 return true; // key pressed
69 }
70
71 if (noteNum > 0 && noteNum <= 96)
72 {
73 recordNote(noteNum, -1);
74 return true; // note key pressed (and note triggered)
75 }
76
77 return false; // no note key pressed
78 }
79
80 // when the cursor is at the note slot
testNoteKeysRelease(SDL_Scancode scancode)81 void testNoteKeysRelease(SDL_Scancode scancode)
82 {
83 const int8_t noteNum = scancodeKeyToNote(scancode); // convert key scancode to note number
84 if (noteNum > 0 && noteNum <= 96)
85 recordNote(noteNum, 0); // release note
86 }
87
testEditKeys(SDL_Scancode scancode,SDL_Keycode keycode)88 static bool testEditKeys(SDL_Scancode scancode, SDL_Keycode keycode)
89 {
90 int8_t i;
91
92 if (cursor.object == CURSOR_NOTE)
93 {
94 // the edit cursor is at the note slot
95
96 if (testNoteKeys(scancode))
97 {
98 keyb.keyRepeat = (playMode == PLAYMODE_EDIT); // repeat keys only if in edit mode
99 return true; // we jammed an instrument
100 }
101
102 return false; // no note key pressed, test other keys
103 }
104
105 if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT)
106 return false; // we're not editing, test other keys
107
108 // convert key to slot data
109
110 if (cursor.object == CURSOR_VOL1)
111 {
112 // volume column effect type (mixed keys)
113
114 for (i = 0; i < KEY2VOL_ENTRIES; i++)
115 {
116 if (keycode == key2VolTab[i])
117 break;
118 }
119
120 if (i == KEY2VOL_ENTRIES)
121 i = -1; // invalid key for slot
122 }
123 else if (cursor.object == CURSOR_EFX0)
124 {
125 // effect type (mixed keys)
126
127 for (i = 0; i < KEY2EFX_ENTRIES; i++)
128 {
129 if (keycode == key2EfxTab[i])
130 break;
131 }
132
133 if (i == KEY2EFX_ENTRIES)
134 i = -1; // invalid key for slot
135 }
136 else
137 {
138 // all other slots (hex keys)
139
140 for (i = 0; i < KEY2HEX_ENTRIES; i++)
141 {
142 if (keycode == key2HexTab[i])
143 break;
144 }
145
146 if (i == KEY2HEX_ENTRIES)
147 i = -1; // invalid key for slot
148 }
149
150 if (i == -1 || !allocatePattern(editor.editPattern))
151 return false; // no edit to be done
152
153 // insert slot data
154
155 note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
156 switch (cursor.object)
157 {
158 case CURSOR_INST1:
159 {
160 uint8_t oldVal = p->instr;
161
162 p->instr = (p->instr & 0x0F) | (i << 4);
163 if (p->instr > MAX_INST)
164 p->instr = MAX_INST;
165
166 if (p->instr != oldVal)
167 setSongModifiedFlag();
168 }
169 break;
170
171 case CURSOR_INST2:
172 {
173 uint8_t oldVal = p->instr;
174 p->instr = (p->instr & 0xF0) | i;
175
176 if (p->instr != oldVal)
177 setSongModifiedFlag();
178 }
179 break;
180
181 case CURSOR_VOL1:
182 {
183 uint8_t oldVal = p->vol;
184
185 p->vol = (p->vol & 0x0F) | ((i + 1) << 4);
186 if (p->vol >= 0x51 && p->vol <= 0x5F)
187 p->vol = 0x50;
188
189 if (p->vol != oldVal)
190 setSongModifiedFlag();
191 }
192 break;
193
194 case CURSOR_VOL2:
195 {
196 uint8_t oldVal = p->vol;
197
198 if (p->vol < 0x10)
199 p->vol = 0x10 + i;
200 else
201 p->vol = (p->vol & 0xF0) | i;
202
203 if (p->vol >= 0x51 && p->vol <= 0x5F)
204 p->vol = 0x50;
205
206 if (p->vol != oldVal)
207 setSongModifiedFlag();
208 }
209 break;
210
211 case CURSOR_EFX0:
212 {
213 uint8_t oldVal = p->efx;
214
215 p->efx = i;
216 if (p->efx != oldVal)
217 setSongModifiedFlag();
218 }
219 break;
220
221 case CURSOR_EFX1:
222 {
223 uint8_t oldVal = p->efxData;
224
225 p->efxData = (p->efxData & 0x0F) | (i << 4);
226 if (p->efxData != oldVal)
227 setSongModifiedFlag();
228 }
229 break;
230
231 case CURSOR_EFX2:
232 {
233 uint8_t oldVal = p->efxData;
234
235 p->efxData = (p->efxData & 0xF0) | i;
236 if (p->efxData != oldVal)
237 setSongModifiedFlag();
238 }
239 break;
240
241 default: break;
242 }
243
244 // increase row (only in edit mode)
245
246 const int16_t numRows = patternNumRows[editor.editPattern];
247 if (playMode == PLAYMODE_EDIT && numRows >= 1)
248 setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
249
250 if (i == 0) // if we inserted a zero, check if pattern is empty, for killing
251 killPatternIfUnused(editor.editPattern);
252
253 ui.updatePatternEditor = true;
254 return true;
255 }
256
evaluateTimeStamp(int16_t * songPos,int16_t * pattNum,int16_t * row,int16_t * tick)257 static void evaluateTimeStamp(int16_t *songPos, int16_t *pattNum, int16_t *row, int16_t *tick)
258 {
259 int16_t outSongPos = editor.songPos;
260 int16_t outPattern = editor.editPattern;
261 int16_t outRow = editor.row;
262 int16_t outTick = editor.speed - editor.tick;
263
264 outTick = CLAMP(outTick, 0, editor.speed - 1);
265
266 // this is needed, but also breaks quantization on speed>15
267 if (outTick > 15)
268 outTick = 15;
269
270 const int16_t numRows = patternNumRows[outPattern];
271
272 if (config.recQuant > 0)
273 {
274 if (config.recQuantRes >= 16)
275 {
276 outTick += (editor.speed >> 1) + 1;
277 }
278 else
279 {
280 int16_t r = tickArr[config.recQuantRes-1];
281 int16_t p = outRow & (r - 1);
282
283 if (p < (r >> 1))
284 outRow -= p;
285 else
286 outRow = (outRow + r) - p;
287
288 outTick = 0;
289 }
290 }
291
292 if (outTick > editor.speed)
293 {
294 outTick -= editor.speed;
295 outRow++;
296 }
297
298 if (outRow >= numRows)
299 {
300 outRow = 0;
301
302 if (playMode == PLAYMODE_RECSONG)
303 outSongPos++;
304
305 if (outSongPos >= song.songLength)
306 outSongPos = song.songLoopStart;
307
308 outPattern = song.orders[outSongPos];
309 }
310
311 *songPos = outSongPos;
312 *pattNum = outPattern;
313 *row = outRow;
314 *tick = outTick;
315 }
316
recordNote(uint8_t noteNum,int8_t vol)317 void recordNote(uint8_t noteNum, int8_t vol) // directly ported from the original FT2 code - what a mess, but it works...
318 {
319 int8_t i;
320 int16_t pattNum, songPos, row, tick;
321 int32_t time;
322 note_t *p;
323
324 const int16_t oldRow = editor.row;
325
326 if (songPlaying)
327 {
328 // row quantization
329 evaluateTimeStamp(&songPos, &pattNum, &row, &tick);
330 }
331 else
332 {
333 songPos = editor.songPos;
334 pattNum = editor.editPattern;
335 row = editor.row;
336 tick = 0;
337 }
338
339 bool editmode = (playMode == PLAYMODE_EDIT);
340 bool recmode = (playMode == PLAYMODE_RECSONG) || (playMode == PLAYMODE_RECPATT);
341
342 if (noteNum == NOTE_OFF)
343 vol = 0;
344
345 int8_t c = -1;
346 int8_t k = -1;
347
348 if (editmode || recmode)
349 {
350 // find out what channel is the most suitable in edit/record mode
351
352 if ((config.multiEdit && editmode) || (config.multiRec && recmode))
353 {
354 time = 0x7FFFFFFF;
355 for (i = 0; i < song.numChannels; i++)
356 {
357 if (editor.chnMode[i] && config.multiRecChn[i] && editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0)
358 {
359 c = i;
360 time = editor.keyOffTime[i];
361 }
362 }
363 }
364 else
365 {
366 c = cursor.ch;
367 }
368
369 for (i = 0; i < song.numChannels; i++)
370 {
371 if (noteNum == editor.keyOnTab[i] && config.multiRecChn[i])
372 k = i;
373 }
374 }
375 else
376 {
377 // find out what channel is the most suitable in idle/play mode (jamming)
378 if (config.multiKeyJazz)
379 {
380 time = 0x7FFFFFFF;
381 c = 0;
382
383 if (songPlaying)
384 {
385 for (i = 0; i < song.numChannels; i++)
386 {
387 if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0 && config.multiRecChn[i])
388 {
389 c = i;
390 time = editor.keyOffTime[i];
391 }
392 }
393 }
394
395 if (time == 0x7FFFFFFF)
396 {
397 for (i = 0; i < song.numChannels; i++)
398 {
399 if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0)
400 {
401 c = i;
402 time = editor.keyOffTime[i];
403 }
404 }
405 }
406 }
407 else
408 {
409 c = cursor.ch;
410 }
411
412 for (i = 0; i < song.numChannels; i++)
413 {
414 if (noteNum == editor.keyOnTab[i])
415 k = i;
416 }
417 }
418
419 if (vol != 0)
420 {
421 if (c < 0 || (k >= 0 && (config.multiEdit || (recmode || !editmode))))
422 return;
423
424 // play note
425
426 editor.keyOnTab[c] = noteNum;
427
428 if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row
429 {
430 #ifdef HAS_MIDI
431 playTone(c, editor.curInstr, noteNum, vol, midi.currMIDIVibDepth, midi.currMIDIPitch);
432 #else
433 playTone(c, editor.curInstr, noteNum, vol, 0, 0);
434 #endif
435 }
436
437 if (editmode || recmode)
438 {
439 if (allocatePattern(pattNum))
440 {
441 const int16_t numRows = patternNumRows[pattNum];
442 p = &pattern[pattNum][(row * MAX_CHANNELS) + c];
443
444 // insert data
445 p->note = noteNum;
446 if (editor.curInstr > 0)
447 p->instr = editor.curInstr;
448
449 if (vol >= 0)
450 p->vol = 0x10 + vol;
451
452 if (!recmode)
453 {
454 // increase row (only in edit mode)
455 if (numRows >= 1)
456 setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
457 }
458 else
459 {
460 // apply tick delay for note if quantization is disabled
461 if (!config.recQuant && tick > 0)
462 {
463 p->efx = 0x0E;
464 p->efxData = 0xD0 + (tick & 0x0F);
465 }
466 }
467
468 ui.updatePatternEditor = true;
469 setSongModifiedFlag();
470 }
471 }
472 }
473 else
474 {
475 // note off
476
477 if (k != -1)
478 c = k;
479
480 if (c < 0)
481 return;
482
483 editor.keyOffNr++;
484
485 editor.keyOnTab[c] = 0;
486 editor.keyOffTime[c] = editor.keyOffNr;
487
488 if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row
489 {
490 #ifdef HAS_MIDI
491 playTone(c, editor.curInstr, NOTE_OFF, vol, midi.currMIDIVibDepth, midi.currMIDIPitch);
492 #else
493 playTone(c, editor.curInstr, NOTE_OFF, vol, 0, 0);
494 #endif
495 }
496
497 if (config.recRelease && recmode)
498 {
499 if (allocatePattern(pattNum))
500 {
501 // insert data
502
503 int16_t numRows = patternNumRows[pattNum];
504 p = &pattern[pattNum][(row * MAX_CHANNELS) + c];
505
506 if (p->note != 0)
507 row++;
508
509 if (row >= numRows)
510 {
511 row = 0;
512
513 if (songPlaying)
514 {
515 songPos++;
516 if (songPos >= song.songLength)
517 songPos = song.songLoopStart;
518
519 pattNum = song.orders[songPos];
520 numRows = patternNumRows[pattNum];
521 }
522 }
523
524 p = &pattern[pattNum][(row * MAX_CHANNELS) + c];
525 p->note = NOTE_OFF;
526
527 if (!recmode)
528 {
529 // increase row (only in edit mode)
530 if (numRows >= 1)
531 setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
532 }
533 else
534 {
535 // apply tick delay for note if quantization is disabled
536 if (!config.recQuant && tick > 0)
537 {
538 p->efx = 0x0E;
539 p->efxData = 0xD0 + (tick & 0x0F);
540 }
541 }
542
543 ui.updatePatternEditor = true;
544 setSongModifiedFlag();
545 }
546 }
547 }
548 }
549
handleEditKeys(SDL_Keycode keycode,SDL_Scancode scancode)550 bool handleEditKeys(SDL_Keycode keycode, SDL_Scancode scancode)
551 {
552 // special case for delete - manipulate note data
553 if (keycode == SDLK_DELETE)
554 {
555 if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT)
556 return false; // we're not editing, test other keys
557
558 if (pattern[editor.editPattern] == NULL)
559 return true;
560
561 note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
562
563 if (keyb.leftShiftPressed)
564 {
565 // delete all
566 p->note = p->instr = p->vol = p->efx = p->efxData = 0;
567 }
568 else if (keyb.leftCtrlPressed)
569 {
570 // delete volume column + effect
571 p->vol = 0;
572 p->efx = 0;
573 p->efxData = 0;
574 }
575 else if (keyb.leftAltPressed)
576 {
577 // delete effect
578 p->efx = 0;
579 p->efxData = 0;
580 }
581 else
582 {
583 if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
584 {
585 // delete volume column
586 p->vol = 0;
587 }
588 else
589 {
590 // delete note + instrument
591 p->note = 0;
592 p->instr = 0;
593 }
594 }
595
596 killPatternIfUnused(editor.editPattern);
597
598 // increase row (only in edit mode)
599 const int16_t numRows = patternNumRows[editor.editPattern];
600 if (playMode == PLAYMODE_EDIT && numRows >= 1)
601 setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
602
603 ui.updatePatternEditor = true;
604 setSongModifiedFlag();
605
606 return true;
607 }
608
609 // a kludge for french keyb. layouts to allow writing numbers in the pattern data with left SHIFT
610 const bool frKeybHack = keyb.leftShiftPressed && !keyb.leftAltPressed && !keyb.leftCtrlPressed &&
611 (scancode >= SDL_SCANCODE_1) && (scancode <= SDL_SCANCODE_0);
612
613 if (frKeybHack || !keyb.keyModifierDown)
614 return (testEditKeys(scancode, keycode));
615
616 return false;
617 }
618
writeToMacroSlot(uint8_t slot)619 void writeToMacroSlot(uint8_t slot)
620 {
621 uint16_t writeVol = 0;
622 uint16_t writeEfx = 0;
623
624 if (pattern[editor.editPattern] != NULL)
625 {
626 note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
627 writeVol = p->vol;
628 writeEfx = (p->efx << 8) | p->efxData;
629 }
630
631 if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
632 config.volMacro[slot] = writeVol;
633 else
634 config.comMacro[slot] = writeEfx;
635 }
636
writeFromMacroSlot(uint8_t slot)637 void writeFromMacroSlot(uint8_t slot)
638 {
639 if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT)
640 return;
641
642 if (!allocatePattern(editor.editPattern))
643 return;
644
645 note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
646 if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
647 {
648 p->vol = (uint8_t)config.volMacro[slot];
649 }
650 else
651 {
652 uint8_t efx = (uint8_t)(config.comMacro[slot] >> 8);
653 if (efx > 35)
654 {
655 // illegal effect
656 p->efx = 0;
657 p->efxData = 0;
658 }
659 else
660 {
661 p->efx = efx;
662 p->efxData = config.comMacro[slot] & 0xFF;
663 }
664 }
665
666 const int16_t numRows = patternNumRows[editor.editPattern];
667 if (playMode == PLAYMODE_EDIT && numRows >= 1)
668 setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
669
670 killPatternIfUnused(editor.editPattern);
671
672 ui.updatePatternEditor = true;
673 setSongModifiedFlag();
674 }
675
insertPatternNote(void)676 void insertPatternNote(void)
677 {
678 if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
679 return;
680
681 note_t *p = pattern[editor.editPattern];
682 if (p == NULL)
683 return;
684
685 const int16_t row = editor.row;
686 const int16_t numRows = patternNumRows[editor.editPattern];
687
688 if (numRows > 1)
689 {
690 for (int32_t i = numRows-2; i >= row; i--)
691 p[((i+1) * MAX_CHANNELS) + cursor.ch] = p[(i * MAX_CHANNELS) + cursor.ch];
692 }
693
694 memset(&p[(row * MAX_CHANNELS) + cursor.ch], 0, sizeof (note_t));
695
696 killPatternIfUnused(editor.editPattern);
697
698 ui.updatePatternEditor = true;
699 setSongModifiedFlag();
700 }
701
insertPatternLine(void)702 void insertPatternLine(void)
703 {
704 if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
705 return;
706
707 setPatternLen(editor.editPattern, patternNumRows[editor.editPattern] + config.recTrueInsert); // config.recTrueInsert is 0 or 1
708
709 note_t *p = pattern[editor.editPattern];
710 if (p != NULL)
711 {
712 const int16_t row = editor.row;
713 const int16_t numRows = patternNumRows[editor.editPattern];
714
715 if (numRows > 1)
716 {
717 for (int32_t i = numRows-2; i >= row; i--)
718 {
719 for (int32_t j = 0; j < MAX_CHANNELS; j++)
720 p[((i+1) * MAX_CHANNELS) + j] = p[(i * MAX_CHANNELS) + j];
721 }
722 }
723
724 memset(&p[row * MAX_CHANNELS], 0, TRACK_WIDTH);
725
726 killPatternIfUnused(editor.editPattern);
727 }
728
729 ui.updatePatternEditor = true;
730 setSongModifiedFlag();
731 }
732
deletePatternNote(void)733 void deletePatternNote(void)
734 {
735 if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
736 return;
737
738 int16_t row = editor.row;
739 const int16_t numRows = patternNumRows[editor.editPattern];
740
741 note_t *p = pattern[editor.editPattern];
742 if (p != NULL)
743 {
744 if (row > 0)
745 {
746 row--;
747 editor.row = song.row = row;
748
749 for (int32_t i = row; i < numRows-1; i++)
750 p[(i * MAX_CHANNELS) + cursor.ch] = p[((i+1) * MAX_CHANNELS) + cursor.ch];
751
752 memset(&p[((numRows-1) * MAX_CHANNELS) + cursor.ch], 0, sizeof (note_t));
753 }
754 }
755 else
756 {
757 if (row > 0)
758 {
759 row--;
760 editor.row = song.row = row;
761 }
762 }
763
764 killPatternIfUnused(editor.editPattern);
765
766 ui.updatePatternEditor = true;
767 setSongModifiedFlag();
768 }
769
deletePatternLine(void)770 void deletePatternLine(void)
771 {
772 if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
773 return;
774
775 int16_t row = editor.row;
776 const int16_t numRows = patternNumRows[editor.editPattern];
777
778 note_t *p = pattern[editor.editPattern];
779 if (p != NULL)
780 {
781 if (row > 0)
782 {
783 row--;
784 editor.row = song.row = row;
785
786 for (int32_t i = row; i < numRows-1; i++)
787 {
788 for (int32_t j = 0; j < MAX_CHANNELS; j++)
789 p[(i * MAX_CHANNELS) + j] = p[((i+1) * MAX_CHANNELS) + j];
790 }
791
792 memset(&p[(numRows-1) * MAX_CHANNELS], 0, TRACK_WIDTH);
793 }
794 }
795 else
796 {
797 if (row > 0)
798 {
799 row--;
800 editor.row = song.row = row;
801 }
802 }
803
804 if (config.recTrueInsert && numRows > 1)
805 setPatternLen(editor.editPattern, numRows-1);
806
807 killPatternIfUnused(editor.editPattern);
808
809 ui.updatePatternEditor = true;
810 setSongModifiedFlag();
811 }
812
813 // ----- TRANSPOSE FUNCTIONS -----
814
countOverflowingNotes(uint8_t currInsOnly,uint8_t transpMode,int8_t addVal)815 static void countOverflowingNotes(uint8_t currInsOnly, uint8_t transpMode, int8_t addVal)
816 {
817 transpDelNotes = 0;
818 switch (transpMode)
819 {
820 case TRANSP_TRACK:
821 {
822 note_t *p = pattern[editor.editPattern];
823 if (p == NULL)
824 return; // empty pattern
825
826 p += cursor.ch;
827
828 const int32_t numRows = patternNumRows[editor.editPattern];
829 for (int32_t row = 0; row < numRows; row++, p += MAX_CHANNELS)
830 {
831 if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
832 {
833 if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
834 transpDelNotes++;
835 }
836 }
837 }
838 break;
839
840 case TRANSP_PATT:
841 {
842 note_t *p = pattern[editor.editPattern];
843 if (p == NULL)
844 return; // empty pattern
845
846 const int32_t numRows = patternNumRows[editor.editPattern];
847 const int32_t pitch = MAX_CHANNELS-song.numChannels;
848
849 for (int32_t row = 0; row < numRows; row++, p += pitch)
850 {
851 for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
852 {
853 if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
854 {
855 if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
856 transpDelNotes++;
857 }
858 }
859 }
860 }
861 break;
862
863 case TRANSP_SONG:
864 {
865 const int32_t pitch = MAX_CHANNELS-song.numChannels;
866 for (int32_t i = 0; i < MAX_PATTERNS; i++)
867 {
868 note_t *p = pattern[i];
869 if (p == NULL)
870 continue; // empty pattern
871
872 const int32_t numRows = patternNumRows[i];
873 for (int32_t row = 0; row < numRows; row++, p += pitch)
874 {
875 for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
876 {
877 if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
878 {
879 if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
880 transpDelNotes++;
881 }
882 }
883 }
884 }
885 }
886 break;
887
888 case TRANSP_BLOCK:
889 {
890 if (pattMark.markY1 == pattMark.markY2)
891 return; // no pattern marking
892
893 note_t *p = pattern[editor.editPattern];
894 if (p == NULL)
895 return; // empty pattern
896
897 p += (pattMark.markY1 * MAX_CHANNELS) + pattMark.markX1;
898
899 const int32_t pitch = MAX_CHANNELS - ((pattMark.markX2 + 1) - pattMark.markX1);
900 for (int32_t row = pattMark.markY1; row < pattMark.markY2; row++, p += pitch)
901 {
902 for (int32_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++, p++)
903 {
904 if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
905 {
906 if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
907 transpDelNotes++;
908 }
909 }
910 }
911 }
912 break;
913
914 default: break;
915 }
916 }
917
doTranspose(void)918 void doTranspose(void)
919 {
920 char text[48];
921
922 countOverflowingNotes(lastInsMode, lastTranspMode, lastTranspVal);
923 if (transpDelNotes > 0)
924 {
925 sprintf(text, "%d note(s) will be erased! Proceed?", (int32_t)transpDelNotes);
926 if (okBox(2, "System request", text) != 1)
927 return;
928 }
929
930 // lastTranspVal is never <-12 or >12, so unsigned testing for >96 is safe
931 switch (lastTranspMode)
932 {
933 case TRANSP_TRACK:
934 {
935 note_t *p = pattern[editor.editPattern];
936 if (p == NULL)
937 return; // empty pattern
938
939 p += cursor.ch;
940
941 const int32_t numRows = patternNumRows[editor.editPattern];
942 for (int32_t row = 0; row < numRows; row++, p += MAX_CHANNELS)
943 {
944 uint8_t note = p->note;
945 if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
946 {
947 note += lastTranspVal;
948 if (note > 96)
949 note = 0; // also handles underflow
950
951 p->note = note;
952 }
953 }
954 }
955 break;
956
957 case TRANSP_PATT:
958 {
959 note_t *p = pattern[editor.editPattern];
960 if (p == NULL)
961 return; // empty pattern
962
963 const int32_t numRows = patternNumRows[editor.editPattern];
964 const int32_t pitch = MAX_CHANNELS - song.numChannels;
965
966 for (int32_t row = 0; row < numRows; row++, p += pitch)
967 {
968 for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
969 {
970 uint8_t note = p->note;
971 if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
972 {
973 note += lastTranspVal;
974 if (note > 96)
975 note = 0; // also handles underflow
976
977 p->note = note;
978 }
979 }
980 }
981 }
982 break;
983
984 case TRANSP_SONG:
985 {
986 const int32_t pitch = MAX_CHANNELS - song.numChannels;
987 for (int32_t i = 0; i < MAX_PATTERNS; i++)
988 {
989 note_t *p = pattern[i];
990 if (p == NULL)
991 continue; // empty pattern
992
993 const int32_t numRows = patternNumRows[i];
994 for (int32_t row = 0; row < numRows; row++, p += pitch)
995 {
996 for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
997 {
998 uint8_t note = p->note;
999 if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
1000 {
1001 note += lastTranspVal;
1002 if (note > 96)
1003 note = 0; // also handles underflow
1004
1005 p->note = note;
1006 }
1007 }
1008 }
1009 }
1010 }
1011 break;
1012
1013 case TRANSP_BLOCK:
1014 {
1015 if (pattMark.markY1 == pattMark.markY2)
1016 return; // no pattern marking
1017
1018 note_t *p = pattern[editor.editPattern];
1019 if (p == NULL)
1020 return; // empty pattern
1021
1022 p += (pattMark.markY1 * MAX_CHANNELS) + pattMark.markX1;
1023
1024 const int32_t pitch = MAX_CHANNELS - ((pattMark.markX2 + 1) - pattMark.markX1);
1025 for (int32_t row = pattMark.markY1; row < pattMark.markY2; row++, p += pitch)
1026 {
1027 for (int32_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++, p++)
1028 {
1029 uint8_t note = p->note;
1030 if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
1031 {
1032 note += lastTranspVal;
1033 if (note > 96)
1034 note = 0; // also handles underflow
1035
1036 p->note = note;
1037 }
1038 }
1039 }
1040 }
1041 break;
1042
1043 default: break;
1044 }
1045
1046 ui.updatePatternEditor = true;
1047 setSongModifiedFlag();
1048 }
1049
trackTranspCurInsUp(void)1050 void trackTranspCurInsUp(void)
1051 {
1052 lastTranspMode = TRANSP_TRACK;
1053 lastTranspVal = 1;
1054 lastInsMode = TRANSP_CUR_INST;
1055 doTranspose();
1056 }
1057
trackTranspCurInsDn(void)1058 void trackTranspCurInsDn(void)
1059 {
1060 lastTranspMode = TRANSP_TRACK;
1061 lastTranspVal = -1;
1062 lastInsMode = TRANSP_CUR_INST;
1063 doTranspose();
1064 }
1065
trackTranspCurIns12Up(void)1066 void trackTranspCurIns12Up(void)
1067 {
1068 lastTranspMode = TRANSP_TRACK;
1069 lastTranspVal = 12;
1070 lastInsMode = TRANSP_CUR_INST;
1071 doTranspose();
1072 }
1073
trackTranspCurIns12Dn(void)1074 void trackTranspCurIns12Dn(void)
1075 {
1076 lastTranspMode = TRANSP_TRACK;
1077 lastTranspVal = -12;
1078 lastInsMode = TRANSP_CUR_INST;
1079 doTranspose();
1080 }
1081
trackTranspAllInsUp(void)1082 void trackTranspAllInsUp(void)
1083 {
1084 lastTranspMode = TRANSP_TRACK;
1085 lastTranspVal = 1;
1086 lastInsMode = TRANSP_ALL_INST;
1087 doTranspose();
1088 }
1089
trackTranspAllInsDn(void)1090 void trackTranspAllInsDn(void)
1091 {
1092 lastTranspMode = TRANSP_TRACK;
1093 lastTranspVal = -1;
1094 lastInsMode = TRANSP_ALL_INST;
1095 doTranspose();
1096 }
1097
trackTranspAllIns12Up(void)1098 void trackTranspAllIns12Up(void)
1099 {
1100 lastTranspMode = TRANSP_TRACK;
1101 lastTranspVal = 12;
1102 lastInsMode = TRANSP_ALL_INST;
1103 doTranspose();
1104 }
1105
trackTranspAllIns12Dn(void)1106 void trackTranspAllIns12Dn(void)
1107 {
1108 lastTranspMode = TRANSP_TRACK;
1109 lastTranspVal = -12;
1110 lastInsMode = TRANSP_ALL_INST;
1111 doTranspose();
1112 }
1113
pattTranspCurInsUp(void)1114 void pattTranspCurInsUp(void)
1115 {
1116 lastTranspMode = TRANSP_PATT;
1117 lastTranspVal = 1;
1118 lastInsMode = TRANSP_CUR_INST;
1119 doTranspose();
1120 }
1121
pattTranspCurInsDn(void)1122 void pattTranspCurInsDn(void)
1123 {
1124 lastTranspMode = TRANSP_PATT;
1125 lastTranspVal = -1;
1126 lastInsMode = TRANSP_CUR_INST;
1127 doTranspose();
1128 }
1129
pattTranspCurIns12Up(void)1130 void pattTranspCurIns12Up(void)
1131 {
1132 lastTranspMode = TRANSP_PATT;
1133 lastTranspVal = 12;
1134 lastInsMode = TRANSP_CUR_INST;
1135 doTranspose();
1136 }
1137
pattTranspCurIns12Dn(void)1138 void pattTranspCurIns12Dn(void)
1139 {
1140 lastTranspMode = TRANSP_PATT;
1141 lastTranspVal = -12;
1142 lastInsMode = TRANSP_CUR_INST;
1143 doTranspose();
1144 }
1145
pattTranspAllInsUp(void)1146 void pattTranspAllInsUp(void)
1147 {
1148 lastTranspMode = TRANSP_PATT;
1149 lastTranspVal = 1;
1150 lastInsMode = TRANSP_ALL_INST;
1151 doTranspose();
1152 }
1153
pattTranspAllInsDn(void)1154 void pattTranspAllInsDn(void)
1155 {
1156 lastTranspMode = TRANSP_PATT;
1157 lastTranspVal = -1;
1158 lastInsMode = TRANSP_ALL_INST;
1159 doTranspose();
1160 }
1161
pattTranspAllIns12Up(void)1162 void pattTranspAllIns12Up(void)
1163 {
1164 lastTranspMode = TRANSP_PATT;
1165 lastTranspVal = 12;
1166 lastInsMode = TRANSP_ALL_INST;
1167 doTranspose();
1168 }
1169
pattTranspAllIns12Dn(void)1170 void pattTranspAllIns12Dn(void)
1171 {
1172 lastTranspMode = TRANSP_PATT;
1173 lastTranspVal = -12;
1174 lastInsMode = TRANSP_ALL_INST;
1175 doTranspose();
1176 }
1177
songTranspCurInsUp(void)1178 void songTranspCurInsUp(void)
1179 {
1180 lastTranspMode = TRANSP_SONG;
1181 lastTranspVal = 1;
1182 lastInsMode = TRANSP_CUR_INST;
1183 doTranspose();
1184 }
1185
songTranspCurInsDn(void)1186 void songTranspCurInsDn(void)
1187 {
1188 lastTranspMode = TRANSP_SONG;
1189 lastTranspVal = -1;
1190 lastInsMode = TRANSP_CUR_INST;
1191 doTranspose();
1192 }
1193
songTranspCurIns12Up(void)1194 void songTranspCurIns12Up(void)
1195 {
1196 lastTranspMode = TRANSP_SONG;
1197 lastTranspVal = 12;
1198 lastInsMode = TRANSP_CUR_INST;
1199 doTranspose();
1200 }
1201
songTranspCurIns12Dn(void)1202 void songTranspCurIns12Dn(void)
1203 {
1204 lastTranspMode = TRANSP_SONG;
1205 lastTranspVal = -12;
1206 lastInsMode = TRANSP_CUR_INST;
1207 doTranspose();
1208 }
1209
songTranspAllInsUp(void)1210 void songTranspAllInsUp(void)
1211 {
1212 lastTranspMode = TRANSP_SONG;
1213 lastTranspVal = 1;
1214 lastInsMode = TRANSP_ALL_INST;
1215 doTranspose();
1216 }
1217
songTranspAllInsDn(void)1218 void songTranspAllInsDn(void)
1219 {
1220 lastTranspMode = TRANSP_SONG;
1221 lastTranspVal = -1;
1222 lastInsMode = TRANSP_ALL_INST;
1223 doTranspose();
1224 }
1225
songTranspAllIns12Up(void)1226 void songTranspAllIns12Up(void)
1227 {
1228 lastTranspMode = TRANSP_SONG;
1229 lastTranspVal = 12;
1230 lastInsMode = TRANSP_ALL_INST;
1231 doTranspose();
1232 }
1233
songTranspAllIns12Dn(void)1234 void songTranspAllIns12Dn(void)
1235 {
1236 lastTranspMode = TRANSP_SONG;
1237 lastTranspVal = -12;
1238 lastInsMode = TRANSP_ALL_INST;
1239 doTranspose();
1240 }
1241
blockTranspCurInsUp(void)1242 void blockTranspCurInsUp(void)
1243 {
1244 lastTranspMode = TRANSP_BLOCK;
1245 lastTranspVal = 1;
1246 lastInsMode = TRANSP_CUR_INST;
1247 doTranspose();
1248 }
1249
blockTranspCurInsDn(void)1250 void blockTranspCurInsDn(void)
1251 {
1252 lastTranspMode = TRANSP_BLOCK;
1253 lastTranspVal = -1;
1254 lastInsMode = TRANSP_CUR_INST;
1255 doTranspose();
1256 }
1257
blockTranspCurIns12Up(void)1258 void blockTranspCurIns12Up(void)
1259 {
1260 lastTranspMode = TRANSP_BLOCK;
1261 lastTranspVal = 12;
1262 lastInsMode = TRANSP_CUR_INST;
1263 doTranspose();
1264 }
1265
blockTranspCurIns12Dn(void)1266 void blockTranspCurIns12Dn(void)
1267 {
1268 lastTranspMode = TRANSP_BLOCK;
1269 lastTranspVal = -12;
1270 lastInsMode = TRANSP_CUR_INST;
1271 doTranspose();
1272 }
1273
blockTranspAllInsUp(void)1274 void blockTranspAllInsUp(void)
1275 {
1276 lastTranspMode = TRANSP_BLOCK;
1277 lastTranspVal = 1;
1278 lastInsMode = TRANSP_ALL_INST;
1279 doTranspose();
1280 }
1281
blockTranspAllInsDn(void)1282 void blockTranspAllInsDn(void)
1283 {
1284 lastTranspMode = TRANSP_BLOCK;
1285 lastTranspVal = -1;
1286 lastInsMode = TRANSP_ALL_INST;
1287 doTranspose();
1288 }
1289
blockTranspAllIns12Up(void)1290 void blockTranspAllIns12Up(void)
1291 {
1292 lastTranspMode = TRANSP_BLOCK;
1293 lastTranspVal = 12;
1294 lastInsMode = TRANSP_ALL_INST;
1295 doTranspose();
1296 }
1297
blockTranspAllIns12Dn(void)1298 void blockTranspAllIns12Dn(void)
1299 {
1300 lastTranspMode = TRANSP_BLOCK;
1301 lastTranspVal = -12;
1302 lastInsMode = TRANSP_ALL_INST;
1303 doTranspose();
1304 }
1305
copyNote(note_t * src,note_t * dst)1306 void copyNote(note_t *src, note_t *dst)
1307 {
1308 if (editor.copyMaskEnable)
1309 {
1310 if (editor.copyMask[0]) dst->note = src->note;
1311 if (editor.copyMask[1]) dst->instr = src->instr;
1312 if (editor.copyMask[2]) dst->vol = src->vol;
1313 if (editor.copyMask[3]) dst->efx = src->efx;
1314 if (editor.copyMask[4]) dst->efxData = src->efxData;
1315 }
1316 else
1317 {
1318 *dst = *src;
1319 }
1320 }
1321
pasteNote(note_t * src,note_t * dst)1322 void pasteNote(note_t *src, note_t *dst)
1323 {
1324 if (editor.copyMaskEnable)
1325 {
1326 if (editor.copyMask[0] && (src->note != 0 || !editor.transpMask[0])) dst->note = src->note;
1327 if (editor.copyMask[1] && (src->instr != 0 || !editor.transpMask[1])) dst->instr = src->instr;
1328 if (editor.copyMask[2] && (src->vol != 0 || !editor.transpMask[2])) dst->vol = src->vol;
1329 if (editor.copyMask[3] && (src->efx != 0 || !editor.transpMask[3])) dst->efx = src->efx;
1330 if (editor.copyMask[4] && (src->efxData != 0 || !editor.transpMask[4])) dst->efxData = src->efxData;
1331 }
1332 else
1333 {
1334 *dst = *src;
1335 }
1336 }
1337
cutTrack(void)1338 void cutTrack(void)
1339 {
1340 note_t *p = pattern[editor.editPattern];
1341 if (p == NULL)
1342 return;
1343
1344 const int16_t numRows = patternNumRows[editor.editPattern];
1345
1346 if (config.ptnCutToBuffer)
1347 {
1348 memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (note_t));
1349 for (int16_t i = 0; i < numRows; i++)
1350 copyNote(&p[(i * MAX_CHANNELS) + cursor.ch], &trackCopyBuff[i]);
1351
1352 trkBufLen = numRows;
1353 }
1354
1355 pauseMusic();
1356 for (int16_t i = 0; i < numRows; i++)
1357 pasteNote(&clearNote, &p[(i * MAX_CHANNELS) + cursor.ch]);
1358 resumeMusic();
1359
1360 killPatternIfUnused(editor.editPattern);
1361
1362 ui.updatePatternEditor = true;
1363 setSongModifiedFlag();
1364 }
1365
copyTrack(void)1366 void copyTrack(void)
1367 {
1368 note_t *p = pattern[editor.editPattern];
1369 if (p == NULL)
1370 return;
1371
1372 const int16_t numRows = patternNumRows[editor.editPattern];
1373
1374 memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (note_t));
1375 for (int16_t i = 0; i < numRows; i++)
1376 copyNote(&p[(i * MAX_CHANNELS) + cursor.ch], &trackCopyBuff[i]);
1377
1378 trkBufLen = numRows;
1379 }
1380
pasteTrack(void)1381 void pasteTrack(void)
1382 {
1383 if (trkBufLen == 0 || !allocatePattern(editor.editPattern))
1384 return;
1385
1386 note_t *p = pattern[editor.editPattern];
1387 const int16_t numRows = patternNumRows[editor.editPattern];
1388
1389 pauseMusic();
1390 for (int16_t i = 0; i < numRows; i++)
1391 pasteNote(&trackCopyBuff[i], &p[(i * MAX_CHANNELS) + cursor.ch]);
1392 resumeMusic();
1393
1394 killPatternIfUnused(editor.editPattern);
1395
1396 ui.updatePatternEditor = true;
1397 setSongModifiedFlag();
1398 }
1399
cutPattern(void)1400 void cutPattern(void)
1401 {
1402 note_t *p = pattern[editor.editPattern];
1403 if (p == NULL)
1404 return;
1405
1406 const int16_t numRows = patternNumRows[editor.editPattern];
1407
1408 if (config.ptnCutToBuffer)
1409 {
1410 memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_CHANNELS) * sizeof (note_t));
1411 for (int16_t x = 0; x < song.numChannels; x++)
1412 {
1413 for (int16_t i = 0; i < numRows; i++)
1414 copyNote(&p[(i * MAX_CHANNELS) + x], &ptnCopyBuff[(i * MAX_CHANNELS) + x]);
1415 }
1416
1417 ptnBufLen = numRows;
1418 }
1419
1420 pauseMusic();
1421 for (int16_t x = 0; x < song.numChannels; x++)
1422 {
1423 for (int16_t i = 0; i < numRows; i++)
1424 pasteNote(&clearNote, &p[(i * MAX_CHANNELS) + x]);
1425 }
1426 resumeMusic();
1427
1428 killPatternIfUnused(editor.editPattern);
1429
1430 ui.updatePatternEditor = true;
1431 setSongModifiedFlag();
1432 }
1433
copyPattern(void)1434 void copyPattern(void)
1435 {
1436 note_t *p = pattern[editor.editPattern];
1437 if (p == NULL)
1438 return;
1439
1440 const int16_t numRows = patternNumRows[editor.editPattern];
1441
1442 memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_CHANNELS) * sizeof (note_t));
1443 for (int16_t x = 0; x < song.numChannels; x++)
1444 {
1445 for (int16_t i = 0; i < numRows; i++)
1446 copyNote(&p[(i * MAX_CHANNELS) + x], &ptnCopyBuff[(i * MAX_CHANNELS) + x]);
1447 }
1448
1449 ptnBufLen = numRows;
1450
1451 ui.updatePatternEditor = true;
1452 }
1453
pastePattern(void)1454 void pastePattern(void)
1455 {
1456 if (ptnBufLen == 0)
1457 return;
1458
1459 if (patternNumRows[editor.editPattern] != ptnBufLen)
1460 {
1461 if (okBox(1, "System request", "Change pattern length to copybuffer's length?") == 1)
1462 setPatternLen(editor.editPattern, ptnBufLen);
1463 }
1464
1465 if (!allocatePattern(editor.editPattern))
1466 return;
1467
1468 note_t *p = pattern[editor.editPattern];
1469 const int16_t numRows = patternNumRows[editor.editPattern];
1470
1471 pauseMusic();
1472 for (int16_t x = 0; x < song.numChannels; x++)
1473 {
1474 for (int16_t i = 0; i < numRows; i++)
1475 pasteNote(&ptnCopyBuff[(i * MAX_CHANNELS) + x], &p[(i * MAX_CHANNELS) + x]);
1476 }
1477 resumeMusic();
1478
1479 killPatternIfUnused(editor.editPattern);
1480
1481 ui.updatePatternEditor = true;
1482 setSongModifiedFlag();
1483 }
1484
cutBlock(void)1485 void cutBlock(void)
1486 {
1487 if (pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
1488 return;
1489
1490 note_t *p = pattern[editor.editPattern];
1491 if (p == NULL)
1492 return;
1493
1494 if (config.ptnCutToBuffer)
1495 {
1496 for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++)
1497 {
1498 for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++)
1499 {
1500 assert(x < song.numChannels && y < patternNumRows[editor.editPattern]);
1501 copyNote(&p[(y * MAX_CHANNELS) + x], &blkCopyBuff[((y - pattMark.markY1) * MAX_CHANNELS) + (x - pattMark.markX1)]);
1502 }
1503 }
1504 }
1505
1506 pauseMusic();
1507 for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++)
1508 {
1509 for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++)
1510 pasteNote(&clearNote, &p[(y * MAX_CHANNELS) + x]);
1511 }
1512 resumeMusic();
1513
1514 markXSize = pattMark.markX2 - pattMark.markX1;
1515 markYSize = pattMark.markY2 - pattMark.markY1;
1516 blockCopied = true;
1517
1518 killPatternIfUnused(editor.editPattern);
1519
1520 ui.updatePatternEditor = true;
1521 setSongModifiedFlag();
1522 }
1523
copyBlock(void)1524 void copyBlock(void)
1525 {
1526 if (pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
1527 return;
1528
1529 note_t *p = pattern[editor.editPattern];
1530 if (p == NULL)
1531 return;
1532
1533 for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++)
1534 {
1535 for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++)
1536 {
1537 assert(x < song.numChannels && y < patternNumRows[editor.editPattern]);
1538 copyNote(&p[(y * MAX_CHANNELS) + x], &blkCopyBuff[((y - pattMark.markY1) * MAX_CHANNELS) + (x - pattMark.markX1)]);
1539 }
1540 }
1541
1542 markXSize = pattMark.markX2 - pattMark.markX1;
1543 markYSize = pattMark.markY2 - pattMark.markY1;
1544 blockCopied = true;
1545 }
1546
pasteBlock(void)1547 void pasteBlock(void)
1548 {
1549 if (!blockCopied || !allocatePattern(editor.editPattern))
1550 return;
1551
1552 const int16_t numRows = patternNumRows[editor.editPattern];
1553
1554 const int32_t xpos = cursor.ch;
1555 const int32_t ypos = editor.row;
1556
1557 int32_t j = markXSize;
1558 if (j+xpos >= song.numChannels)
1559 j = song.numChannels - xpos - 1;
1560
1561 int32_t k = markYSize;
1562 if (k+ypos >= numRows)
1563 k = numRows-ypos;
1564
1565 note_t *p = pattern[editor.editPattern];
1566
1567 pauseMusic();
1568 for (int32_t x = xpos; x <= xpos+j; x++)
1569 {
1570 for (int32_t y = ypos; y < ypos+k; y++)
1571 {
1572 assert(x < song.numChannels && y < numRows);
1573 pasteNote(&blkCopyBuff[((y - ypos) * MAX_CHANNELS) + (x - xpos)], &p[(y * MAX_CHANNELS) + x]);
1574 }
1575 }
1576 resumeMusic();
1577
1578 killPatternIfUnused(editor.editPattern);
1579
1580 ui.updatePatternEditor = true;
1581 setSongModifiedFlag();
1582 }
1583
remapInstrXY(uint16_t pattNum,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2,uint8_t src,uint8_t dst)1584 static void remapInstrXY(uint16_t pattNum, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t src, uint8_t dst)
1585 {
1586 // this routine is only used sanely, so no need to check input
1587
1588 note_t *pattPtr = pattern[pattNum];
1589 if (pattPtr == NULL)
1590 return;
1591
1592 note_t *p = &pattPtr[(y1 * MAX_CHANNELS) + x1];
1593
1594 const int32_t pitch = MAX_CHANNELS - ((x2 + 1) - x1);
1595 for (uint16_t y = y1; y <= y2; y++, p += pitch)
1596 {
1597 for (uint16_t x = x1; x <= x2; x++, p++)
1598 {
1599 if (p->instr == src)
1600 p->instr = dst;
1601 }
1602 }
1603 }
1604
remapBlock(void)1605 void remapBlock(void)
1606 {
1607 if (editor.srcInstr == editor.curInstr || pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
1608 return;
1609
1610 pauseMusic();
1611 remapInstrXY(editor.editPattern,
1612 pattMark.markX1, pattMark.markY1,
1613 pattMark.markX2, pattMark.markY2 - 1,
1614 editor.srcInstr, editor.curInstr);
1615 resumeMusic();
1616
1617 ui.updatePatternEditor = true;
1618 setSongModifiedFlag();
1619 }
1620
remapTrack(void)1621 void remapTrack(void)
1622 {
1623 if (editor.srcInstr == editor.curInstr)
1624 return;
1625
1626 pauseMusic();
1627 remapInstrXY(editor.editPattern,
1628 cursor.ch, 0,
1629 cursor.ch, patternNumRows[editor.editPattern] - 1,
1630 editor.srcInstr, editor.curInstr);
1631 resumeMusic();
1632
1633 ui.updatePatternEditor = true;
1634 setSongModifiedFlag();
1635 }
1636
remapPattern(void)1637 void remapPattern(void)
1638 {
1639 if (editor.srcInstr == editor.curInstr)
1640 return;
1641
1642 pauseMusic();
1643 remapInstrXY(editor.editPattern,
1644 0, 0,
1645 (uint16_t)(song.numChannels - 1), patternNumRows[editor.editPattern] - 1,
1646 editor.srcInstr, editor.curInstr);
1647 resumeMusic();
1648
1649 ui.updatePatternEditor = true;
1650 setSongModifiedFlag();
1651 }
1652
remapSong(void)1653 void remapSong(void)
1654 {
1655 if (editor.srcInstr == editor.curInstr)
1656 return;
1657
1658 pauseMusic();
1659 for (int32_t i = 0; i < MAX_PATTERNS; i++)
1660 {
1661 const uint8_t pattNum = (uint8_t)i;
1662
1663 remapInstrXY(pattNum,
1664 0, 0,
1665 (uint16_t)(song.numChannels - 1), patternNumRows[pattNum] - 1,
1666 editor.srcInstr, editor.curInstr);
1667 }
1668 resumeMusic();
1669
1670 ui.updatePatternEditor = true;
1671 setSongModifiedFlag();
1672 }
1673
1674 // "scale-fade volume" routines
1675
getNoteVolume(note_t * p)1676 static int8_t getNoteVolume(note_t *p)
1677 {
1678 int8_t nv, vv, ev;
1679
1680 if (p->vol >= 0x10 && p->vol <= 0x50)
1681 vv = p->vol - 0x10;
1682 else
1683 vv = -1;
1684
1685 if (p->efx == 0xC)
1686 ev = MIN(p->efxData, 64);
1687 else
1688 ev = -1;
1689
1690 if (p->instr != 0 && instr[p->instr] != NULL)
1691 nv = (int8_t)instr[p->instr]->smp[0].volume;
1692 else
1693 nv = -1;
1694
1695 int8_t finalv = -1;
1696 if (nv >= 0) finalv = nv;
1697 if (vv >= 0) finalv = vv;
1698 if (ev >= 0) finalv = ev;
1699
1700 return finalv;
1701 }
1702
setNoteVolume(note_t * p,int8_t newVol)1703 static void setNoteVolume(note_t *p, int8_t newVol)
1704 {
1705 if (newVol < 0)
1706 return;
1707
1708 const int8_t oldv = getNoteVolume(p);
1709 if (p->vol == oldv)
1710 return; // volume is the same
1711
1712 if (p->efx == 0x0C)
1713 p->efxData = newVol; // Cxx effect
1714 else
1715 p->vol = 0x10 + newVol; // volume column
1716 }
1717
scaleNote(uint16_t pattNum,int8_t ch,int16_t row,double dScale)1718 static void scaleNote(uint16_t pattNum, int8_t ch, int16_t row, double dScale)
1719 {
1720 if (pattern[pattNum] == NULL)
1721 return;
1722
1723 const int16_t numRows = patternNumRows[pattNum];
1724 if (row < 0 || row >= numRows || ch < 0 || ch >= song.numChannels)
1725 return;
1726
1727 note_t *p = &pattern[pattNum][(row * MAX_CHANNELS) + ch];
1728
1729 int32_t vol = getNoteVolume(p);
1730 if (vol >= 0)
1731 {
1732 vol = (int32_t)((vol * dScale) + 0.5); // rounded
1733 vol = MIN(MAX(0, vol), 64);
1734 setNoteVolume(p, (int8_t)vol);
1735 }
1736 }
1737
askForScaleFade(char * msg)1738 static bool askForScaleFade(char *msg)
1739 {
1740 char volstr[32+1];
1741
1742 sprintf(volstr, "%0.2f,%0.2f", dVolScaleFK1, dVolScaleFK2);
1743 if (inputBox(1, msg, volstr, sizeof (volstr) - 1) != 1)
1744 return false;
1745
1746 bool err = false;
1747
1748 char *val1 = volstr;
1749 if (strlen(val1) < 3)
1750 err = true;
1751
1752 char *val2 = strchr(volstr, ',');
1753 if (val2 == NULL || strlen(val2) < 3)
1754 err = true;
1755
1756 if (err)
1757 {
1758 okBox(0, "System message", "Invalid constant expressions.");
1759 return false;
1760 }
1761
1762 dVolScaleFK1 = atof(val1+0);
1763 dVolScaleFK2 = atof(val2+1);
1764
1765 return true;
1766 }
1767
scaleFadeVolumeTrack(void)1768 void scaleFadeVolumeTrack(void)
1769 {
1770 if (!askForScaleFade("Volume scale-fade track (start-, end scale)"))
1771 return;
1772
1773 if (pattern[editor.editPattern] == NULL)
1774 return;
1775
1776 const int32_t numRows = patternNumRows[editor.editPattern];
1777
1778 double dVolDelta = 0.0;
1779 if (numRows > 0)
1780 dVolDelta = (dVolScaleFK2 - dVolScaleFK1) / numRows;
1781
1782 double dVol = dVolScaleFK1;
1783
1784 pauseMusic();
1785 for (int16_t row = 0; row < numRows; row++)
1786 {
1787 scaleNote(editor.editPattern, cursor.ch, row, dVol);
1788 dVol += dVolDelta;
1789 }
1790 resumeMusic();
1791 }
1792
scaleFadeVolumePattern(void)1793 void scaleFadeVolumePattern(void)
1794 {
1795 if (!askForScaleFade("Volume scale-fade pattern (start-, end scale)"))
1796 return;
1797
1798 if (pattern[editor.editPattern] == NULL)
1799 return;
1800
1801 const int32_t numRows = patternNumRows[editor.editPattern];
1802
1803 double dVolDelta = 0.0;
1804 if (numRows > 0)
1805 dVolDelta = (dVolScaleFK2 - dVolScaleFK1) / numRows;
1806
1807 double dVol = dVolScaleFK1;
1808
1809 pauseMusic();
1810 for (int16_t row = 0; row < numRows; row++)
1811 {
1812 for (int8_t ch = 0; ch < song.numChannels; ch++)
1813 scaleNote(editor.editPattern, ch, row, dVol);
1814
1815 dVol += dVolDelta;
1816 }
1817 resumeMusic();
1818 }
1819
scaleFadeVolumeBlock(void)1820 void scaleFadeVolumeBlock(void)
1821 {
1822 if (!askForScaleFade("Volume scale-fade block (start-, end scale)"))
1823 return;
1824
1825 if (pattern[editor.editPattern] == NULL || pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
1826 return;
1827
1828 const int32_t numRows = pattMark.markY2 - pattMark.markY1;
1829
1830 double dVolDelta = 0.0;
1831 if (numRows > 0)
1832 dVolDelta = (dVolScaleFK2 - dVolScaleFK1) / numRows;
1833
1834 double dVol = dVolScaleFK1;
1835
1836 pauseMusic();
1837 for (int16_t row = pattMark.markY1; row < pattMark.markY2; row++)
1838 {
1839 for (int16_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++)
1840 scaleNote(editor.editPattern, (uint8_t)ch, row, dVol);
1841
1842 dVol += dVolDelta;
1843 }
1844 resumeMusic();
1845 }
1846
toggleCopyMaskEnable(void)1847 void toggleCopyMaskEnable(void) { editor.copyMaskEnable ^= 1; }
toggleCopyMask0(void)1848 void toggleCopyMask0(void) { editor.copyMask[0] ^= 1; };
toggleCopyMask1(void)1849 void toggleCopyMask1(void) { editor.copyMask[1] ^= 1; };
toggleCopyMask2(void)1850 void toggleCopyMask2(void) { editor.copyMask[2] ^= 1; };
toggleCopyMask3(void)1851 void toggleCopyMask3(void) { editor.copyMask[3] ^= 1; };
toggleCopyMask4(void)1852 void toggleCopyMask4(void) { editor.copyMask[4] ^= 1; };
togglePasteMask0(void)1853 void togglePasteMask0(void) { editor.pasteMask[0] ^= 1; };
togglePasteMask1(void)1854 void togglePasteMask1(void) { editor.pasteMask[1] ^= 1; };
togglePasteMask2(void)1855 void togglePasteMask2(void) { editor.pasteMask[2] ^= 1; };
togglePasteMask3(void)1856 void togglePasteMask3(void) { editor.pasteMask[3] ^= 1; };
togglePasteMask4(void)1857 void togglePasteMask4(void) { editor.pasteMask[4] ^= 1; };
toggleTranspMask0(void)1858 void toggleTranspMask0(void) { editor.transpMask[0] ^= 1; };
toggleTranspMask1(void)1859 void toggleTranspMask1(void) { editor.transpMask[1] ^= 1; };
toggleTranspMask2(void)1860 void toggleTranspMask2(void) { editor.transpMask[2] ^= 1; };
toggleTranspMask3(void)1861 void toggleTranspMask3(void) { editor.transpMask[3] ^= 1; };
toggleTranspMask4(void)1862 void toggleTranspMask4(void) { editor.transpMask[4] ^= 1; };
1863