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 <stdint.h>
7 #include <stdbool.h>
8 #include <fcntl.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #ifdef _WIN32
12 #include <direct.h>
13 #else
14 #include <unistd.h>
15 #endif
16 #include "pt2_header.h"
17 #include "pt2_helpers.h"
18 #include "pt2_textout.h"
19 #include "pt2_tables.h"
20 #include "pt2_audio.h"
21 #include "pt2_diskop.h"
22 #include "pt2_mouse.h"
23 #include "pt2_sampler.h"
24 #include "pt2_visuals.h"
25 #include "pt2_keyboard.h"
26 #include "pt2_scopes.h"
27 #include "pt2_structs.h"
28 #include "pt2_config.h"
29 #include "pt2_audio.h"
30 #include "pt2_sync.h"
31 #include "pt2_chordmaker.h"
32 
33 const int8_t scancode2NoteLo[52] = // "USB usage page standard" order
34 {
35 	 7,  4,  3, 16, -1,  6,  8, 24,
36 	10, -1, 13, 11,  9, 26, 28, 12,
37 	17,  1, 19, 23,  5, 14,  2, 21,
38 	 0, -1, 13, 15, -1, 18, 20, 22,
39 	-1, 25, 27, -1, -1, -1, -1, -1,
40 	-1, 30, 29, 31, 30, -1, 15, -1,
41 	-1, 12, 14, 16
42 };
43 
44 const int8_t scancode2NoteHi[52] = // "USB usage page standard" order
45 {
46 	19, 16, 15, 28, -1, 18, 20, -2,
47 	22, -1, 25, 23, 21, -2, -2, 24,
48 	29, 13, 31, 35, 17, 26, 14, 33,
49 	12, -1, 25, 27, -1, 30, 32, 34,
50 	-1, -2, -2, -1, -1, -1, -1, -1,
51 	-1, -2, -2, -2, -2, -1, 27, -1,
52 	-1, 24, 26, 28
53 };
54 
55 void setPattern(int16_t pattern); // pt2_replayer.c
56 
57 void jamAndPlaceSample(SDL_Scancode scancode,  bool normalMode);
58 uint8_t quantizeCheck(uint8_t row);
59 bool handleSpecialKeys(SDL_Scancode scancode);
60 int8_t keyToNote(SDL_Scancode scancode);
61 
62 // used for re-rendering text object while editing it
updateTextObject(int16_t editObject)63 void updateTextObject(int16_t editObject)
64 {
65 	switch (editObject)
66 	{
67 		default: break;
68 		case PTB_SONGNAME: ui.updateSongName = true; break;
69 		case PTB_SAMPLENAME: ui.updateCurrSampleName = true; break;
70 		case PTB_PE_PATT: ui.updatePosEd = true; break;
71 		case PTB_EO_QUANTIZE: ui.updateQuantizeText = true; break;
72 		case PTB_EO_METRO_1: ui.updateMetro1Text = true; break;
73 		case PTB_EO_METRO_2: ui.updateMetro2Text = true; break;
74 		case PTB_EO_FROM_NUM: ui.updateFromText = true; break;
75 		case PTB_EO_TO_NUM: ui.updateToText = true; break;
76 		case PTB_EO_MIX: ui.updateMixText = true; break;
77 		case PTB_EO_POS_NUM: ui.updatePosText = true; break;
78 		case PTB_EO_MOD_NUM: ui.updateModText = true; break;
79 		case PTB_EO_VOL_NUM: ui.updateVolText = true; break;
80 		case PTB_DO_DATAPATH: ui.updateDiskOpPathText = true; break;
81 		case PTB_POSS: ui.updateSongPos = true; break;
82 		case PTB_PATTERNS: ui.updateSongPattern = true; break;
83 		case PTB_LENGTHS: ui.updateSongLength = true; break;
84 		case PTB_SAMPLES: ui.updateCurrSampleNum = true; break;
85 		case PTB_SVOLUMES: ui.updateCurrSampleVolume = true; break;
86 		case PTB_SLENGTHS: ui.updateCurrSampleLength = true; break;
87 		case PTB_SREPEATS: ui.updateCurrSampleRepeat = true; break;
88 		case PTB_SREPLENS: ui.updateCurrSampleReplen = true; break;
89 		case PTB_PATTDATA: ui.updateCurrPattText = true; break;
90 		case PTB_SA_VOL_FROM_NUM: ui.updateVolFromText = true; break;
91 		case PTB_SA_VOL_TO_NUM: ui.updateVolToText = true; break;
92 		case PTB_SA_FIL_LP_CUTOFF: ui.updateLPText = true; break;
93 		case PTB_SA_FIL_HP_CUTOFF: ui.updateHPText = true; break;
94 	}
95 }
96 
exitGetTextLine(bool updateValue)97 void exitGetTextLine(bool updateValue)
98 {
99 	int8_t tmp8;
100 	int16_t posEdPos, tmp16;
101 	int32_t tmp32;
102 	UNICHAR *pathU;
103 	moduleSample_t *s;
104 
105 	SDL_StopTextInput();
106 
107 	// if user updated the disk op path text
108 	if (ui.diskOpScreenShown && ui.editObject == PTB_DO_DATAPATH)
109 	{
110 		pathU = (UNICHAR *)calloc(PATH_MAX + 2, sizeof (UNICHAR));
111 		if (pathU != NULL)
112 		{
113 #ifdef _WIN32
114 			MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, editor.currPath, -1, pathU, PATH_MAX);
115 #else
116 			strcpy(pathU, editor.currPath);
117 #endif
118 			diskOpSetPath(pathU, DISKOP_CACHE);
119 			free(pathU);
120 		}
121 	}
122 
123 	if (ui.editTextType != TEXT_EDIT_STRING)
124 	{
125 		if (ui.dstPos != ui.numLen)
126 			removeTextEditMarker();
127 
128 		updateTextObject(ui.editObject);
129 	}
130 	else
131 	{
132 		removeTextEditMarker();
133 
134 		// yet another kludge...
135 		if (ui.editObject == PTB_PE_PATT)
136 			ui.updatePosEd = true;
137 	}
138 
139 	ui.editTextFlag = false;
140 
141 	ui.lineCurX = 0;
142 	ui.lineCurY = 0;
143 	ui.editPos = NULL;
144 	ui.dstPos = 0;
145 
146 	if (ui.editTextType == TEXT_EDIT_STRING)
147 	{
148 		if (ui.dstOffset != NULL)
149 			*ui.dstOffset = '\0';
150 
151 		pointerSetPreviousMode();
152 
153 		if (!editor.mixFlag)
154 			updateWindowTitle(MOD_IS_MODIFIED);
155 	}
156 	else
157 	{
158 		// set back GUI text pointers and update values (if requested)
159 
160 		s = &song->samples[editor.currSample];
161 		switch (ui.editObject)
162 		{
163 			case PTB_SA_FIL_LP_CUTOFF:
164 			{
165 				editor.lpCutOffDisp = &editor.lpCutOff;
166 
167 				if (updateValue)
168 				{
169 					editor.lpCutOff = ui.tmpDisp16;
170 					if (editor.lpCutOff > (uint16_t)(FILTERS_BASE_FREQ/2))
171 						editor.lpCutOff = (uint16_t)(FILTERS_BASE_FREQ/2);
172 
173 					ui.updateLPText = true;
174 				}
175 			}
176 			break;
177 
178 			case PTB_SA_FIL_HP_CUTOFF:
179 			{
180 				editor.hpCutOffDisp = &editor.hpCutOff;
181 
182 				if (updateValue)
183 				{
184 					editor.hpCutOff = ui.tmpDisp16;
185 					if (editor.hpCutOff > (uint16_t)(FILTERS_BASE_FREQ/2))
186 						editor.hpCutOff = (uint16_t)(FILTERS_BASE_FREQ/2);
187 
188 					ui.updateHPText = true;
189 				}
190 			}
191 			break;
192 
193 			case PTB_SA_VOL_FROM_NUM:
194 			{
195 				editor.vol1Disp = &editor.vol1;
196 
197 				if (updateValue)
198 				{
199 					editor.vol1 = ui.tmpDisp16;
200 					if (editor.vol1 > 200)
201 						editor.vol1 = 200;
202 
203 					ui.updateVolFromText = true;
204 					showVolFromSlider();
205 				}
206 			}
207 			break;
208 
209 			case PTB_SA_VOL_TO_NUM:
210 			{
211 				editor.vol2Disp = &editor.vol2;
212 
213 				if (updateValue)
214 				{
215 					editor.vol2 = ui.tmpDisp16;
216 					if (editor.vol2 > 200)
217 						editor.vol2 = 200;
218 
219 					ui.updateVolToText = true;
220 					showVolToSlider();
221 				}
222 			}
223 			break;
224 
225 			case PTB_EO_VOL_NUM:
226 			{
227 				editor.sampleVolDisp = &editor.sampleVol;
228 
229 				if (updateValue)
230 				{
231 					editor.sampleVol = ui.tmpDisp16;
232 					ui.updateVolText = true;
233 				}
234 			}
235 			break;
236 
237 			case PTB_EO_POS_NUM:
238 			{
239 				editor.samplePosDisp = &editor.samplePos;
240 
241 				if (updateValue)
242 				{
243 					editor.samplePos = ui.tmpDisp16;
244 					if (editor.samplePos > song->samples[editor.currSample].length)
245 						editor.samplePos = song->samples[editor.currSample].length;
246 
247 					ui.updatePosText = true;
248 				}
249 			}
250 			break;
251 
252 			case PTB_EO_QUANTIZE:
253 			{
254 				editor.quantizeValueDisp = &config.quantizeValue;
255 
256 				if (updateValue)
257 				{
258 					if (ui.tmpDisp16 > 63)
259 						ui.tmpDisp16 = 63;
260 
261 					config.quantizeValue = ui.tmpDisp16;
262 					ui.updateQuantizeText = true;
263 				}
264 			}
265 			break;
266 
267 			case PTB_EO_METRO_1: // metronome speed
268 			{
269 				editor.metroSpeedDisp = &editor.metroSpeed;
270 
271 				if (updateValue)
272 				{
273 					if (ui.tmpDisp16 > 64)
274 						ui.tmpDisp16 = 64;
275 
276 					editor.metroSpeed = ui.tmpDisp16;
277 					ui.updateMetro1Text = true;
278 				}
279 			}
280 			break;
281 
282 			case PTB_EO_METRO_2: // metronome channel
283 			{
284 				editor.metroChannelDisp = &editor.metroChannel;
285 
286 				if (updateValue)
287 				{
288 					if (ui.tmpDisp16 > 4)
289 						ui.tmpDisp16 = 4;
290 
291 					editor.metroChannel = ui.tmpDisp16;
292 					ui.updateMetro2Text = true;
293 				}
294 			}
295 			break;
296 
297 			case PTB_EO_FROM_NUM:
298 			{
299 				editor.sampleFromDisp = &editor.sampleFrom;
300 
301 				if (updateValue)
302 				{
303 					editor.sampleFrom = ui.tmpDisp8;
304 
305 					// signed check + normal check
306 					if (editor.sampleFrom < 0x00 || editor.sampleFrom > 0x1F)
307 						editor.sampleFrom = 0x1F;
308 
309 					ui.updateFromText = true;
310 				}
311 			}
312 			break;
313 
314 			case PTB_EO_TO_NUM:
315 			{
316 				editor.sampleToDisp = &editor.sampleTo;
317 
318 				if (updateValue)
319 				{
320 					editor.sampleTo = ui.tmpDisp8;
321 
322 					// signed check + normal check
323 					if (editor.sampleTo < 0x00 || editor.sampleTo > 0x1F)
324 						editor.sampleTo = 0x1F;
325 
326 					ui.updateToText = true;
327 				}
328 			}
329 			break;
330 
331 			case PTB_PE_PATT:
332 			{
333 				posEdPos = song->currOrder;
334 				if (posEdPos > song->header.numOrders-1)
335 					posEdPos = song->header.numOrders-1;
336 
337 				editor.currPosEdPattDisp = &song->header.order[posEdPos];
338 
339 				if (updateValue)
340 				{
341 					if (ui.tmpDisp16 > MAX_PATTERNS-1)
342 						ui.tmpDisp16 = MAX_PATTERNS-1;
343 
344 					song->header.order[posEdPos] = ui.tmpDisp16;
345 
346 					updateWindowTitle(MOD_IS_MODIFIED);
347 
348 					if (ui.posEdScreenShown)
349 						ui.updatePosEd = true;
350 
351 					ui.updateSongPattern = true;
352 					ui.updateSongSize = true;
353 				}
354 			}
355 			break;
356 
357 			case PTB_POSS:
358 			{
359 				editor.currPosDisp = &song->currOrder;
360 
361 				if (updateValue)
362 				{
363 					tmp16 = ui.tmpDisp16;
364 					if (tmp16 > 126)
365 						tmp16 = 126;
366 
367 					if (song->currOrder != tmp16)
368 					{
369 						song->currOrder = tmp16;
370 						editor.currPatternDisp = &song->header.order[song->currOrder];
371 
372 						if (ui.posEdScreenShown)
373 							ui.updatePosEd = true;
374 
375 						ui.updateSongPos = true;
376 						ui.updatePatternData = true;
377 					}
378 				}
379 			}
380 			break;
381 
382 			case PTB_PATTERNS:
383 			{
384 				editor.currPatternDisp = &song->header.order[song->currOrder];
385 
386 				if (updateValue)
387 				{
388 					tmp16 = ui.tmpDisp16;
389 					if (tmp16 > MAX_PATTERNS-1)
390 						tmp16 = MAX_PATTERNS-1;
391 
392 					if (song->header.order[song->currOrder] != tmp16)
393 					{
394 						song->header.order[song->currOrder] = tmp16;
395 
396 						updateWindowTitle(MOD_IS_MODIFIED);
397 
398 						if (ui.posEdScreenShown)
399 							ui.updatePosEd = true;
400 
401 						ui.updateSongPattern = true;
402 						ui.updateSongSize = true;
403 					}
404 				}
405 			}
406 			break;
407 
408 			case PTB_LENGTHS:
409 			{
410 				editor.currLengthDisp = &song->header.numOrders;
411 
412 				if (updateValue)
413 				{
414 					tmp16 = CLAMP(ui.tmpDisp16, 1, 127);
415 
416 					if (song->header.numOrders != tmp16)
417 					{
418 						song->header.numOrders = tmp16;
419 
420 						posEdPos = song->currOrder;
421 						if (posEdPos > song->header.numOrders-1)
422 							posEdPos = song->header.numOrders-1;
423 
424 						editor.currPosEdPattDisp = &song->header.order[posEdPos];
425 
426 						if (ui.posEdScreenShown)
427 							ui.updatePosEd = true;
428 
429 						ui.updateSongLength = true;
430 						ui.updateSongSize = true;
431 						updateWindowTitle(MOD_IS_MODIFIED);
432 					}
433 				}
434 			}
435 			break;
436 
437 			case PTB_PATTDATA:
438 			{
439 				editor.currEditPatternDisp = &song->currPattern;
440 
441 				if (updateValue)
442 				{
443 					if (song->currPattern != ui.tmpDisp16)
444 					{
445 						setPattern(ui.tmpDisp16);
446 						ui.updatePatternData = true;
447 						ui.updateCurrPattText = true;
448 					}
449 				}
450 			}
451 			break;
452 
453 			case PTB_SAMPLES:
454 			{
455 				editor.currSampleDisp = &editor.currSample;
456 
457 				if (updateValue)
458 				{
459 					tmp8 = ui.tmpDisp8;
460 					if (tmp8 < 0x00) // (signed) if >0x7F was entered, clamp to 0x1F
461 						tmp8 = 0x1F;
462 
463 					tmp8 = CLAMP(tmp8, 0x01, 0x1F) - 1;
464 
465 					if (tmp8 != editor.currSample)
466 					{
467 						editor.currSample = tmp8;
468 						updateCurrSample();
469 					}
470 				}
471 			}
472 			break;
473 
474 			case PTB_SVOLUMES:
475 			{
476 				s->volumeDisp = &s->volume;
477 
478 				if (updateValue)
479 				{
480 					tmp8 = ui.tmpDisp8;
481 
482 					// signed check + normal check
483 					if (tmp8 < 0x00 || tmp8 > 0x40)
484 						 tmp8 = 0x40;
485 
486 					if (s->volume != tmp8)
487 					{
488 						s->volume = tmp8;
489 						ui.updateCurrSampleVolume = true;
490 						updateWindowTitle(MOD_IS_MODIFIED);
491 					}
492 				}
493 			}
494 			break;
495 
496 			case PTB_SLENGTHS:
497 			{
498 				s->lengthDisp = &s->length;
499 
500 				if (updateValue)
501 				{
502 					tmp32 = ui.tmpDisp16 & 0xFFFE; // even'ify
503 
504 					if (s->loopStart+s->loopLength > 2)
505 					{
506 						if (tmp32 < s->loopStart+s->loopLength)
507 							tmp32 = s->loopStart+s->loopLength;
508 					}
509 
510 					tmp32 &= 0xFFFE;
511 
512 					if (s->length != tmp32)
513 					{
514 						turnOffVoices();
515 						s->length = (uint16_t)tmp32;
516 
517 						ui.updateCurrSampleLength = true;
518 						ui.updateSongSize = true;
519 						updateSamplePos();
520 
521 						if (ui.samplerScreenShown)
522 							redrawSample();
523 
524 						recalcChordLength();
525 						updateWindowTitle(MOD_IS_MODIFIED);
526 					}
527 				}
528 			}
529 			break;
530 
531 			case PTB_SREPEATS:
532 			{
533 				s->loopStartDisp = &s->loopStart;
534 
535 				if (updateValue)
536 				{
537 					tmp32 = ui.tmpDisp16 & 0xFFFE; // even'ify
538 
539 					if (s->length >= s->loopLength)
540 					{
541 						if (tmp32+s->loopLength > s->length)
542 							 tmp32 = s->length - s->loopLength;
543 					}
544 					else
545 					{
546 						tmp32 = 0;
547 					}
548 
549 					tmp32 &= 0xFFFE;
550 
551 					if (s->loopStart != tmp32)
552 					{
553 						turnOffVoices();
554 						s->loopStart = (uint16_t)tmp32;
555 						mixerUpdateLoops();
556 
557 						ui.updateCurrSampleRepeat = true;
558 
559 						if (ui.editOpScreenShown && ui.editOpScreen == 3)
560 							ui.updateLengthText = true;
561 
562 						if (ui.samplerScreenShown)
563 							setLoopSprites();
564 
565 						updateWindowTitle(MOD_IS_MODIFIED);
566 					}
567 				}
568 			}
569 			break;
570 
571 			case PTB_SREPLENS:
572 			{
573 				s->loopLengthDisp = &s->loopLength;
574 
575 				if (updateValue)
576 				{
577 					tmp32 = ui.tmpDisp16 & 0xFFFE; // even'ify
578 
579 					if (s->length >= s->loopStart)
580 					{
581 						if (s->loopStart+tmp32 > s->length)
582 							tmp32 = s->length - s->loopStart;
583 					}
584 					else
585 					{
586 						tmp32 = 2;
587 					}
588 
589 					tmp32 &= 0xFFFE;
590 
591 					if (tmp32 < 2)
592 						tmp32 = 2;
593 
594 					if (s->loopLength != tmp32)
595 					{
596 						turnOffVoices();
597 						s->loopLength = (uint16_t)tmp32;
598 						mixerUpdateLoops();
599 
600 						ui.updateCurrSampleReplen = true;
601 						if (ui.editOpScreenShown && ui.editOpScreen == 3)
602 							ui.updateLengthText = true;
603 
604 						if (ui.samplerScreenShown)
605 							setLoopSprites();
606 
607 						updateWindowTitle(MOD_IS_MODIFIED);
608 					}
609 				}
610 			}
611 			break;
612 
613 			default: break;
614 		}
615 
616 		pointerSetPreviousMode();
617 	}
618 
619 	ui.editTextType = 0;
620 }
621 
getTextLine(int16_t editObject)622 void getTextLine(int16_t editObject)
623 {
624 	pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
625 
626 	ui.lineCurY = (ui.editTextPos / 40) + 5;
627 	ui.lineCurX = ((ui.editTextPos % 40) * FONT_CHAR_W) + 4;
628 	ui.dstPtr = ui.showTextPtr;
629 	ui.editPos = ui.showTextPtr;
630 	ui.dstPos = 0;
631 	ui.editTextFlag = true;
632 	ui.editTextType = TEXT_EDIT_STRING;
633 	ui.editObject = editObject;
634 
635 	if (ui.dstOffset != NULL)
636 		ui.dstOffset[0] = '\0';
637 
638 	// kludge
639 	if (editor.mixFlag)
640 	{
641 		textCharNext();
642 		textCharNext();
643 		textCharNext();
644 		textCharNext();
645 	}
646 
647 	renderTextEditMarker();
648 	SDL_StartTextInput();
649 }
650 
getNumLine(uint8_t type,int16_t editObject)651 void getNumLine(uint8_t type, int16_t editObject)
652 {
653 	pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
654 
655 	ui.lineCurY = (ui.editTextPos / 40) + 5;
656 	ui.lineCurX = ((ui.editTextPos % 40) * FONT_CHAR_W) + 4;
657 	ui.dstPos = 0;
658 	ui.editTextFlag = true;
659 	ui.editTextType = type;
660 	ui.editObject = editObject;
661 
662 	renderTextEditMarker();
663 	SDL_StartTextInput();
664 }
665 
handleEditKeys(SDL_Scancode scancode,bool normalMode)666 void handleEditKeys(SDL_Scancode scancode, bool normalMode)
667 {
668 	int8_t key, hexKey, numberKey;
669 	note_t *note;
670 
671 	if (ui.editTextFlag)
672 		return;
673 
674 	if (ui.samplerScreenShown || (editor.currMode == MODE_IDLE || editor.currMode == MODE_PLAY))
675 	{
676 		// at this point it will only jam, not place it
677 		if (!keyb.leftAltPressed && !keyb.leftAmigaPressed && !keyb.leftCtrlPressed && !keyb.shiftPressed)
678 			jamAndPlaceSample(scancode, normalMode);
679 
680 		return;
681 	}
682 
683 	// handle modified (ALT/CTRL/SHIFT etc) keys for editing
684 	if (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD)
685 	{
686 		if (handleSpecialKeys(scancode))
687 		{
688 			if (editor.currMode != MODE_RECORD)
689 				modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
690 
691 			return;
692 		}
693 	}
694 
695 	// are we editing a note, or other stuff?
696 	if (cursor.mode != CURSOR_NOTE)
697 	{
698 		// if we held down any key modifier at this point, then do nothing
699 		if (keyb.leftAltPressed || keyb.leftAmigaPressed || keyb.leftCtrlPressed || keyb.shiftPressed)
700 			return;
701 
702 		if (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD)
703 		{
704 			if (scancode == SDL_SCANCODE_0)
705 				numberKey = 0;
706 			else if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_9)
707 				numberKey = (int8_t)scancode - (SDL_SCANCODE_1-1);
708 			else
709 				numberKey = -1;
710 
711 			if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_F)
712 				hexKey = 10 + ((int8_t)scancode - SDL_SCANCODE_A);
713 			else
714 				hexKey = -1;
715 
716 			key = -1;
717 			if (numberKey != -1)
718 			{
719 				if (key == -1)
720 					key = 0;
721 
722 				key += numberKey;
723 			}
724 
725 			if (hexKey != -1)
726 			{
727 				if (key == -1)
728 					key = 0;
729 
730 				key += hexKey;
731 			}
732 
733 			note = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
734 
735 			switch (cursor.mode)
736 			{
737 				case CURSOR_SAMPLE1:
738 				{
739 					if (key != -1 && key < 2)
740 					{
741 						note->sample = (uint8_t)((note->sample % 0x10) | (key << 4));
742 
743 						if (editor.currMode != MODE_RECORD)
744 							modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
745 
746 						updateWindowTitle(MOD_IS_MODIFIED);
747 					}
748 				}
749 				break;
750 
751 				case CURSOR_SAMPLE2:
752 				{
753 					if (key != -1 && key < 16)
754 					{
755 						note->sample = (uint8_t)((note->sample & 16) | key);
756 
757 						if (editor.currMode != MODE_RECORD)
758 							modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
759 
760 						updateWindowTitle(MOD_IS_MODIFIED);
761 					}
762 				}
763 				break;
764 
765 				case CURSOR_CMD:
766 				{
767 					if (key != -1 && key < 16)
768 					{
769 						note->command = (uint8_t)key;
770 
771 						if (editor.currMode != MODE_RECORD)
772 							modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
773 
774 						updateWindowTitle(MOD_IS_MODIFIED);
775 					}
776 				}
777 				break;
778 
779 				case CURSOR_PARAM1:
780 				{
781 					if (key != -1 && key < 16)
782 					{
783 						note->param = (uint8_t)((note->param % 0x10) | (key << 4));
784 
785 						if (editor.currMode != MODE_RECORD)
786 							modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
787 
788 						updateWindowTitle(MOD_IS_MODIFIED);
789 					}
790 				}
791 				break;
792 
793 				case CURSOR_PARAM2:
794 				{
795 					if (key != -1 && key < 16)
796 					{
797 						note->param = (uint8_t)((note->param & 0xF0) | key);
798 
799 						if (editor.currMode != MODE_RECORD)
800 							modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
801 
802 						updateWindowTitle(MOD_IS_MODIFIED);
803 					}
804 				}
805 				break;
806 
807 				default: break;
808 			}
809 		}
810 	}
811 	else
812 	{
813 		if (scancode == SDL_SCANCODE_DELETE)
814 		{
815 			if (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD)
816 			{
817 				note = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
818 
819 				if (!keyb.leftAltPressed)
820 				{
821 					note->sample = 0;
822 					note->period = 0;
823 				}
824 
825 				if (keyb.shiftPressed || keyb.leftAltPressed)
826 				{
827 					note->command = 0;
828 					note->param = 0;
829 				}
830 
831 				if (editor.currMode != MODE_RECORD)
832 					modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
833 
834 				updateWindowTitle(MOD_IS_MODIFIED);
835 			}
836 		}
837 		else
838 		{
839 			// if we held down any key modifier at this point, then do nothing
840 			if (keyb.leftAltPressed || keyb.leftAmigaPressed || keyb.leftCtrlPressed || keyb.shiftPressed)
841 				return;
842 
843 			jamAndPlaceSample(scancode, normalMode);
844 		}
845 	}
846 }
847 
handleSpecialKeys(SDL_Scancode scancode)848 bool handleSpecialKeys(SDL_Scancode scancode)
849 {
850 	note_t *patt, *note, *prevNote;
851 
852 	if (!keyb.leftAltPressed)
853 		return false;
854 
855 	patt = song->patterns[song->currPattern];
856 	note = &patt[(song->currRow * AMIGA_VOICES) + cursor.channel];
857 	prevNote = &patt[(((song->currRow - 1) & 0x3F) * AMIGA_VOICES) + cursor.channel];
858 
859 	if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0)
860 	{
861 		// insert stored effect (buffer[0..8])
862 		note->command = editor.effectMacros[scancode - SDL_SCANCODE_1] >> 8;
863 		note->param = editor.effectMacros[scancode - SDL_SCANCODE_1] & 0xFF;
864 
865 		updateWindowTitle(MOD_IS_MODIFIED);
866 		return true;
867 	}
868 
869 	// copy command+effect from above into current command+effect
870 	if (scancode == SDL_SCANCODE_BACKSLASH)
871 	{
872 		note->command = prevNote->command;
873 		note->param = prevNote->param;
874 
875 		updateWindowTitle(MOD_IS_MODIFIED);
876 		return true;
877 	}
878 
879 	// copy command+(effect + 1) from above into current command+effect
880 	if (scancode == SDL_SCANCODE_EQUALS)
881 	{
882 		note->command = prevNote->command;
883 		note->param = prevNote->param + 1; // wraps 0x00..0xFF
884 
885 		updateWindowTitle(MOD_IS_MODIFIED);
886 		return true;
887 	}
888 
889 	// copy command+(effect - 1) from above into current command+effect
890 	if (scancode == SDL_SCANCODE_MINUS)
891 	{
892 		note->command = prevNote->command;
893 		note->param = prevNote->param - 1; // wraps 0x00..0xFF
894 
895 		updateWindowTitle(MOD_IS_MODIFIED);
896 		return true;
897 	}
898 
899 	return false;
900 }
901 
handleSampleJamming(SDL_Scancode scancode)902 void handleSampleJamming(SDL_Scancode scancode) // used for the sampling feature (in SAMPLER)
903 {
904 	const int32_t ch = cursor.channel;
905 
906 	if (scancode == SDL_SCANCODE_NONUSBACKSLASH)
907 	{
908 		turnOffVoices(); // magic "kill all voices" button
909 		return;
910 	}
911 
912 	const int8_t noteVal = keyToNote(scancode);
913 	if (noteVal < 0 || noteVal > 35)
914 		return;
915 
916 	moduleSample_t *s = &song->samples[editor.currSample];
917 	if (s->length <= 1)
918 		return;
919 
920 	lockAudio();
921 
922 	song->channels[ch].n_samplenum = editor.currSample; // needed for sample playback/sampling line
923 
924 	const int8_t *n_start = &song->sampleData[s->offset];
925 	const int8_t vol = 64;
926 	const uint16_t n_length = s->length >> 1;
927 	const uint16_t period = periodTable[((s->fineTune & 0xF) * 37) + noteVal];
928 
929 	paulaSetVolume(ch, vol);
930 	paulaSetPeriod(ch, period);
931 	paulaSetData(ch, n_start);
932 	paulaSetLength(ch, n_length);
933 
934 	if (!editor.muted[ch])
935 		paulaStartDMA(ch);
936 	else
937 		paulaStopDMA(ch);
938 
939 	// these take effect after the current DMA cycle is done
940 	paulaSetData(ch, NULL);
941 	paulaSetLength(ch, 1);
942 
943 	unlockAudio();
944 }
945 
jamAndPlaceSample(SDL_Scancode scancode,bool normalMode)946 void jamAndPlaceSample(SDL_Scancode scancode, bool normalMode)
947 {
948 	int8_t noteVal;
949 	uint8_t ch;
950 	int16_t tempPeriod;
951 	uint16_t cleanPeriod;
952 	moduleChannel_t *chn;
953 	moduleSample_t *s;
954 	note_t *note;
955 
956 	ch = cursor.channel;
957 	assert(ch < AMIGA_VOICES);
958 
959 	chn = &song->channels[ch];
960 	note = &song->patterns[song->currPattern][(quantizeCheck(song->currRow) * AMIGA_VOICES) + ch];
961 
962 	noteVal = normalMode ? keyToNote(scancode) : pNoteTable[editor.currSample];
963 	if (noteVal >= 0)
964 	{
965 		s = &song->samples[editor.currSample];
966 
967 		tempPeriod  = periodTable[((s->fineTune & 0xF) * 37) + noteVal];
968 		cleanPeriod = periodTable[noteVal];
969 
970 		editor.currPlayNote = noteVal;
971 
972 		// play current sample
973 
974 		// don't play sample if we quantized to another row (will be played in modplayer instead)
975 		if (editor.currMode != MODE_RECORD || !editor.didQuantize)
976 		{
977 			lockAudio();
978 
979 			chn->n_samplenum = editor.currSample;
980 			chn->n_volume = s->volume;
981 			chn->n_period = tempPeriod;
982 			chn->n_start = &song->sampleData[s->offset];
983 			chn->n_length = (s->loopStart > 0) ? (s->loopStart + s->loopLength) >> 1 : s->length >> 1;
984 			chn->n_loopstart = &song->sampleData[s->offset + s->loopStart];
985 			chn->n_replen = s->loopLength >> 1;
986 
987 			if (chn->n_length == 0)
988 				chn->n_length = 1;
989 
990 			paulaSetVolume(ch, chn->n_volume);
991 			paulaSetPeriod(ch, chn->n_period);
992 			paulaSetData(ch, chn->n_start);
993 			paulaSetLength(ch, chn->n_length);
994 
995 			if (!editor.muted[ch])
996 				paulaStartDMA(ch);
997 			else
998 				paulaStopDMA(ch);
999 
1000 			// these take effect after the current DMA cycle is done
1001 			paulaSetData(ch, chn->n_loopstart);
1002 			paulaSetLength(ch, chn->n_replen);
1003 
1004 			unlockAudio();
1005 		}
1006 
1007 		// normalMode = normal keys, or else keypad keys (in jam mode)
1008 		if (normalMode || editor.pNoteFlag != 0)
1009 		{
1010 			if (normalMode || editor.pNoteFlag == 2)
1011 			{
1012 				// insert note and sample number
1013 				if (!ui.samplerScreenShown && (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD))
1014 				{
1015 					note->sample = editor.sampleZero ? 0 : (editor.currSample + 1);
1016 					note->period = cleanPeriod;
1017 
1018 					if (editor.autoInsFlag)
1019 					{
1020 						note->command = editor.effectMacros[editor.autoInsSlot] >> 8;
1021 						note->param = editor.effectMacros[editor.autoInsSlot] & 0xFF;
1022 					}
1023 
1024 					if (editor.currMode != MODE_RECORD)
1025 						modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
1026 
1027 					updateWindowTitle(MOD_IS_MODIFIED);
1028 				}
1029 			}
1030 
1031 			if (editor.multiFlag)
1032 				gotoNextMulti();
1033 		}
1034 
1035 		updateSpectrumAnalyzer(s->volume, tempPeriod);
1036 	}
1037 	else if (noteVal == -2)
1038 	{
1039 		// delete note and sample if illegal note (= -2, -1 = ignore) key was entered
1040 
1041 		if (normalMode || editor.pNoteFlag == 2)
1042 		{
1043 			if (!ui.samplerScreenShown && (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD))
1044 			{
1045 				note->period = 0;
1046 				note->sample = 0;
1047 
1048 				if (editor.currMode != MODE_RECORD)
1049 					modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
1050 
1051 				updateWindowTitle(MOD_IS_MODIFIED);
1052 			}
1053 		}
1054 	}
1055 }
1056 
quantizeCheck(uint8_t row)1057 uint8_t quantizeCheck(uint8_t row)
1058 {
1059 	assert(song != NULL);
1060 	if (song == NULL)
1061 		return row;
1062 
1063 	const uint8_t quantize = (uint8_t)config.quantizeValue;
1064 
1065 	editor.didQuantize = false;
1066 	if (editor.currMode == MODE_RECORD)
1067 	{
1068 		if (quantize == 0)
1069 		{
1070 			return row;
1071 		}
1072 		else if (quantize == 1)
1073 		{
1074 			if (song->tick > song->speed>>1)
1075 			{
1076 				row = (row + 1) & 0x3F;
1077 				editor.didQuantize = true;
1078 			}
1079 		}
1080 		else
1081 		{
1082 			uint8_t tempRow = ((((quantize >> 1) + row) & 0x3F) / quantize) * quantize;
1083 			if (tempRow > row)
1084 				editor.didQuantize = true;
1085 
1086 			return tempRow;
1087 		}
1088 	}
1089 
1090 	return row;
1091 }
1092 
saveUndo(void)1093 void saveUndo(void)
1094 {
1095 	memcpy(editor.undoBuffer, song->patterns[song->currPattern], sizeof (note_t) * (AMIGA_VOICES * MOD_ROWS));
1096 }
1097 
undoLastChange(void)1098 void undoLastChange(void)
1099 {
1100 	note_t data;
1101 
1102 	for (uint16_t i = 0; i < MOD_ROWS*AMIGA_VOICES; i++)
1103 	{
1104 		data = editor.undoBuffer[i];
1105 		editor.undoBuffer[i] = song->patterns[song->currPattern][i];
1106 		song->patterns[song->currPattern][i] = data;
1107 	}
1108 
1109 	updateWindowTitle(MOD_IS_MODIFIED);
1110 	ui.updatePatternData = true;
1111 }
1112 
copySampleTrack(void)1113 void copySampleTrack(void)
1114 {
1115 	uint8_t i;
1116 	uint32_t tmpOffset;
1117 	note_t *noteSrc;
1118 	moduleSample_t *smpFrom, *smpTo;
1119 
1120 	if (editor.trackPattFlag == 2)
1121 	{
1122 		// copy from one sample slot to another
1123 
1124 		// never attempt to swap if from and/or to is 0
1125 		if (editor.sampleFrom == 0 || editor.sampleTo == 0)
1126 		{
1127 			displayErrorMsg("FROM/TO = 0 !");
1128 			return;
1129 		}
1130 
1131 		smpTo = &song->samples[editor.sampleTo - 1];
1132 		smpFrom = &song->samples[editor.sampleFrom - 1];
1133 
1134 		turnOffVoices();
1135 
1136 		// copy
1137 		tmpOffset = smpTo->offset;
1138 		*smpTo = *smpFrom;
1139 		smpTo->offset = tmpOffset;
1140 
1141 		// update the copied sample's GUI text pointers
1142 		smpTo->volumeDisp = &smpTo->volume;
1143 		smpTo->lengthDisp = &smpTo->length;
1144 		smpTo->loopStartDisp = &smpTo->loopStart;
1145 		smpTo->loopLengthDisp = &smpTo->loopLength;
1146 
1147 		// copy sample data
1148 		memcpy(&song->sampleData[smpTo->offset], &song->sampleData[smpFrom->offset], MAX_SAMPLE_LEN);
1149 
1150 		updateCurrSample();
1151 		ui.updateSongSize = true;
1152 	}
1153 	else
1154 	{
1155 		// copy sample number in track/pattern
1156 		if (editor.trackPattFlag == 0)
1157 		{
1158 			for (i = 0; i < MOD_ROWS; i++)
1159 			{
1160 				noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
1161 				if (noteSrc->sample == editor.sampleFrom)
1162 					noteSrc->sample = editor.sampleTo;
1163 			}
1164 		}
1165 		else
1166 		{
1167 			for (i = 0; i < AMIGA_VOICES; i++)
1168 			{
1169 				for (uint8_t j = 0; j < MOD_ROWS; j++)
1170 				{
1171 					noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
1172 					if (noteSrc->sample == editor.sampleFrom)
1173 						noteSrc->sample = editor.sampleTo;
1174 				}
1175 			}
1176 		}
1177 
1178 		ui.updatePatternData = true;
1179 	}
1180 
1181 	editor.samplePos = 0;
1182 	updateSamplePos();
1183 
1184 	updateWindowTitle(MOD_IS_MODIFIED);
1185 }
1186 
exchSampleTrack(void)1187 void exchSampleTrack(void)
1188 {
1189 	int8_t smp;
1190 	uint32_t i, tmpOffset;
1191 	moduleSample_t *smpFrom, *smpTo, smpTmp;
1192 	note_t *noteSrc;
1193 
1194 	if (editor.trackPattFlag == 2)
1195 	{
1196 		// exchange sample slots
1197 
1198 		// never attempt to swap if from and/or to is 0
1199 		if (editor.sampleFrom == 0 || editor.sampleTo == 0)
1200 		{
1201 			displayErrorMsg("FROM/TO = 0 !");
1202 			return;
1203 		}
1204 
1205 		smpTo = &song->samples[editor.sampleTo-1];
1206 		smpFrom = &song->samples[editor.sampleFrom-1];
1207 
1208 		turnOffVoices();
1209 
1210 		// swap offsets first so that the next swap will leave offsets intact
1211 		tmpOffset = smpFrom->offset;
1212 		smpFrom->offset = smpTo->offset;
1213 		smpTo->offset = tmpOffset;
1214 
1215 		// swap sample (now offsets are left as before)
1216 		smpTmp = *smpFrom;
1217 		*smpFrom = *smpTo;
1218 		*smpTo = smpTmp;
1219 
1220 		// update the swapped sample's GUI text pointers
1221 		smpFrom->volumeDisp = &smpFrom->volume;
1222 		smpFrom->lengthDisp = &smpFrom->length;
1223 		smpFrom->loopStartDisp = &smpFrom->loopStart;
1224 		smpFrom->loopLengthDisp = &smpFrom->loopLength;
1225 		smpTo->volumeDisp = &smpTo->volume;
1226 		smpTo->lengthDisp = &smpTo->length;
1227 		smpTo->loopStartDisp = &smpTo->loopStart;
1228 		smpTo->loopLengthDisp = &smpTo->loopLength;
1229 
1230 		// swap sample data
1231 		for (i = 0; i < MAX_SAMPLE_LEN; i++)
1232 		{
1233 			smp = song->sampleData[smpFrom->offset+i];
1234 			song->sampleData[smpFrom->offset+i] = song->sampleData[smpTo->offset+i];
1235 			song->sampleData[smpTo->offset+i] = smp;
1236 		}
1237 
1238 		editor.sampleZero = false;
1239 
1240 		updateCurrSample();
1241 	}
1242 	else
1243 	{
1244 		// exchange sample number in track/pattern
1245 		if (editor.trackPattFlag == 0)
1246 		{
1247 			for (i = 0; i < MOD_ROWS; i++)
1248 			{
1249 				noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
1250 
1251 				     if (noteSrc->sample == editor.sampleFrom) noteSrc->sample = editor.sampleTo;
1252 				else if (noteSrc->sample == editor.sampleTo) noteSrc->sample = editor.sampleFrom;
1253 			}
1254 		}
1255 		else
1256 		{
1257 			for (i = 0; i < AMIGA_VOICES; i++)
1258 			{
1259 				for (uint8_t j = 0; j < MOD_ROWS; j++)
1260 				{
1261 					noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
1262 
1263 					     if (noteSrc->sample == editor.sampleFrom) noteSrc->sample = editor.sampleTo;
1264 					else if (noteSrc->sample == editor.sampleTo) noteSrc->sample = editor.sampleFrom;
1265 				}
1266 			}
1267 		}
1268 
1269 		ui.updatePatternData = true;
1270 	}
1271 
1272 	editor.samplePos = 0;
1273 	updateSamplePos();
1274 
1275 	updateWindowTitle(MOD_IS_MODIFIED);
1276 }
1277 
delSampleTrack(void)1278 void delSampleTrack(void)
1279 {
1280 	uint8_t i;
1281 	note_t *noteSrc;
1282 
1283 	saveUndo();
1284 	if (editor.trackPattFlag == 0)
1285 	{
1286 		for (i = 0; i < MOD_ROWS; i++)
1287 		{
1288 			noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
1289 			if (noteSrc->sample == editor.currSample+1)
1290 			{
1291 				noteSrc->period = 0;
1292 				noteSrc->sample = 0;
1293 				noteSrc->command = 0;
1294 				noteSrc->param = 0;
1295 			}
1296 		}
1297 	}
1298 	else
1299 	{
1300 		for (i = 0; i < AMIGA_VOICES; i++)
1301 		{
1302 			for (uint8_t j = 0; j < MOD_ROWS; j++)
1303 			{
1304 				noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
1305 				if (noteSrc->sample == editor.currSample+1)
1306 				{
1307 					noteSrc->period = 0;
1308 					noteSrc->sample = 0;
1309 					noteSrc->command = 0;
1310 					noteSrc->param = 0;
1311 				}
1312 			}
1313 		}
1314 	}
1315 
1316 	updateWindowTitle(MOD_IS_MODIFIED);
1317 	ui.updatePatternData = true;
1318 }
1319 
trackNoteUp(bool sampleAllFlag,uint8_t from,uint8_t to)1320 void trackNoteUp(bool sampleAllFlag, uint8_t from, uint8_t to)
1321 {
1322 	bool noteDeleted;
1323 	uint8_t j;
1324 	note_t *noteSrc;
1325 
1326 	if (from > to)
1327 	{
1328 		j = from;
1329 		from = to;
1330 		to = j;
1331 	}
1332 
1333 	saveUndo();
1334 	for (uint8_t i = from; i <= to; i++)
1335 	{
1336 		noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
1337 
1338 		if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
1339 			continue;
1340 
1341 		if (noteSrc->period)
1342 		{
1343 			// period -> note
1344 			for (j = 0; j < 36; j++)
1345 			{
1346 				if (noteSrc->period >= periodTable[j])
1347 					break;
1348 			}
1349 
1350 			noteDeleted = false;
1351 			if (++j > 35)
1352 			{
1353 				j = 35;
1354 
1355 				if (config.transDel)
1356 				{
1357 					noteSrc->period = 0;
1358 					noteSrc->sample = 0;
1359 
1360 					noteDeleted = true;
1361 				}
1362 			}
1363 
1364 			if (!noteDeleted)
1365 				noteSrc->period = periodTable[j];
1366 		}
1367 	}
1368 
1369 	updateWindowTitle(MOD_IS_MODIFIED);
1370 	ui.updatePatternData = true;
1371 }
1372 
trackNoteDown(bool sampleAllFlag,uint8_t from,uint8_t to)1373 void trackNoteDown(bool sampleAllFlag, uint8_t from, uint8_t to)
1374 {
1375 	bool noteDeleted;
1376 	int8_t j;
1377 	note_t *noteSrc;
1378 
1379 	if (from > to)
1380 	{
1381 		j = from;
1382 		from = to;
1383 		to = j;
1384 	}
1385 
1386 	saveUndo();
1387 	for (uint8_t i = from; i <= to; i++)
1388 	{
1389 		noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
1390 
1391 		if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
1392 			continue;
1393 
1394 		if (noteSrc->period)
1395 		{
1396 			// period -> note
1397 			for (j = 0; j < 36; j++)
1398 			{
1399 				if (noteSrc->period >= periodTable[j])
1400 					break;
1401 			}
1402 
1403 			noteDeleted = false;
1404 			if (--j < 0)
1405 			{
1406 				j = 0;
1407 
1408 				if (config.transDel)
1409 				{
1410 					noteSrc->period = 0;
1411 					noteSrc->sample = 0;
1412 
1413 					noteDeleted = true;
1414 				}
1415 			}
1416 
1417 			if (!noteDeleted)
1418 				noteSrc->period = periodTable[j];
1419 		}
1420 	}
1421 
1422 	updateWindowTitle(MOD_IS_MODIFIED);
1423 	ui.updatePatternData = true;
1424 }
1425 
trackOctaUp(bool sampleAllFlag,uint8_t from,uint8_t to)1426 void trackOctaUp(bool sampleAllFlag, uint8_t from, uint8_t to)
1427 {
1428 	bool noteDeleted, noteChanged;
1429 	uint8_t j;
1430 	note_t *noteSrc;
1431 
1432 	if (from > to)
1433 	{
1434 		j = from;
1435 		from = to;
1436 		to = j;
1437 	}
1438 
1439 	noteChanged = false;
1440 
1441 	saveUndo();
1442 	for (uint8_t i = from; i <= to; i++)
1443 	{
1444 		noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
1445 
1446 		if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
1447 			continue;
1448 
1449 		if (noteSrc->period)
1450 		{
1451 			uint16_t oldPeriod = noteSrc->period;
1452 
1453 			// period -> note
1454 			for (j = 0; j < 36; j++)
1455 			{
1456 				if (noteSrc->period >= periodTable[j])
1457 					break;
1458 			}
1459 
1460 			noteDeleted = false;
1461 			if (j+12 > 35 && config.transDel)
1462 			{
1463 				noteSrc->period = 0;
1464 				noteSrc->sample = 0;
1465 
1466 				noteDeleted = true;
1467 			}
1468 
1469 			if (j <= 23)
1470 				j += 12;
1471 
1472 			if (!noteDeleted)
1473 				noteSrc->period = periodTable[j];
1474 
1475 			if (noteSrc->period != oldPeriod)
1476 				noteChanged = true;
1477 		}
1478 	}
1479 
1480 	if (noteChanged)
1481 	{
1482 		updateWindowTitle(MOD_IS_MODIFIED);
1483 		ui.updatePatternData = true;
1484 	}
1485 }
1486 
trackOctaDown(bool sampleAllFlag,uint8_t from,uint8_t to)1487 void trackOctaDown(bool sampleAllFlag, uint8_t from, uint8_t to)
1488 {
1489 	bool noteDeleted;
1490 	int8_t j;
1491 	note_t *noteSrc;
1492 
1493 	if (from > to)
1494 	{
1495 		j = from;
1496 		from = to;
1497 		to = j;
1498 	}
1499 
1500 	saveUndo();
1501 	for (uint8_t i = from; i <= to; i++)
1502 	{
1503 		noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
1504 
1505 		if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
1506 			continue;
1507 
1508 		if (noteSrc->period)
1509 		{
1510 			// period -> note
1511 			for (j = 0; j < 36; j++)
1512 			{
1513 				if (noteSrc->period >= periodTable[j])
1514 					break;
1515 			}
1516 
1517 			noteDeleted = false;
1518 			if (j-12 < 0 && config.transDel)
1519 			{
1520 				noteSrc->period = 0;
1521 				noteSrc->sample = 0;
1522 
1523 				noteDeleted = true;
1524 			}
1525 
1526 			if (j >= 12)
1527 				j -= 12;
1528 
1529 			if (!noteDeleted)
1530 				noteSrc->period = periodTable[j];
1531 		}
1532 	}
1533 
1534 	updateWindowTitle(MOD_IS_MODIFIED);
1535 	ui.updatePatternData = true;
1536 }
1537 
pattNoteUp(bool sampleAllFlag)1538 void pattNoteUp(bool sampleAllFlag)
1539 {
1540 	bool noteDeleted;
1541 	uint8_t k;
1542 	note_t *noteSrc;
1543 
1544 	saveUndo();
1545 	for (uint8_t i = 0; i < AMIGA_VOICES; i++)
1546 	{
1547 		for (uint8_t j = 0; j < MOD_ROWS; j++)
1548 		{
1549 			noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
1550 
1551 			if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
1552 				continue;
1553 
1554 			if (noteSrc->period)
1555 			{
1556 				// period -> note
1557 				for (k = 0; k < 36; k++)
1558 				{
1559 					if (noteSrc->period >= periodTable[k])
1560 						break;
1561 				}
1562 
1563 				noteDeleted = false;
1564 				if (++k > 35)
1565 				{
1566 					k = 35;
1567 
1568 					if (config.transDel)
1569 					{
1570 						noteSrc->period = 0;
1571 						noteSrc->sample = 0;
1572 
1573 						noteDeleted = true;
1574 					}
1575 				}
1576 
1577 				if (!noteDeleted)
1578 					noteSrc->period = periodTable[k];
1579 			}
1580 		}
1581 	}
1582 
1583 	updateWindowTitle(MOD_IS_MODIFIED);
1584 	ui.updatePatternData = true;
1585 }
1586 
pattNoteDown(bool sampleAllFlag)1587 void pattNoteDown(bool sampleAllFlag)
1588 {
1589 	bool noteDeleted;
1590 	int8_t k;
1591 	note_t *noteSrc;
1592 
1593 	saveUndo();
1594 	for (uint8_t i = 0; i < AMIGA_VOICES; i++)
1595 	{
1596 		for (uint8_t j = 0; j < MOD_ROWS; j++)
1597 		{
1598 			noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
1599 
1600 			if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
1601 				continue;
1602 
1603 			if (noteSrc->period)
1604 			{
1605 				// period -> note
1606 				for (k = 0; k < 36; k++)
1607 				{
1608 					if (noteSrc->period >= periodTable[k])
1609 						break;
1610 				}
1611 
1612 				noteDeleted = false;
1613 				if (--k < 0)
1614 				{
1615 					k = 0;
1616 
1617 					if (config.transDel)
1618 					{
1619 						noteSrc->period = 0;
1620 						noteSrc->sample = 0;
1621 
1622 						noteDeleted = true;
1623 					}
1624 				}
1625 
1626 				if (!noteDeleted)
1627 					noteSrc->period = periodTable[k];
1628 			}
1629 		}
1630 	}
1631 
1632 	updateWindowTitle(MOD_IS_MODIFIED);
1633 	ui.updatePatternData = true;
1634 }
1635 
pattOctaUp(bool sampleAllFlag)1636 void pattOctaUp(bool sampleAllFlag)
1637 {
1638 	bool noteDeleted;
1639 	uint8_t k;
1640 	note_t *noteSrc;
1641 
1642 	saveUndo();
1643 	for (uint8_t i = 0; i < AMIGA_VOICES; i++)
1644 	{
1645 		for (uint8_t j = 0; j < MOD_ROWS; j++)
1646 		{
1647 			noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
1648 
1649 			if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
1650 				continue;
1651 
1652 			if (noteSrc->period)
1653 			{
1654 				// period -> note
1655 				for (k = 0; k < 36; k++)
1656 				{
1657 					if (noteSrc->period >= periodTable[k])
1658 						break;
1659 				}
1660 
1661 				noteDeleted = false;
1662 				if (k+12 > 35 && config.transDel)
1663 				{
1664 					noteSrc->period = 0;
1665 					noteSrc->sample = 0;
1666 
1667 					noteDeleted = true;
1668 				}
1669 
1670 				if (k <= 23)
1671 					k += 12;
1672 
1673 				if (!noteDeleted)
1674 					noteSrc->period = periodTable[k];
1675 			}
1676 		}
1677 	}
1678 
1679 	updateWindowTitle(MOD_IS_MODIFIED);
1680 	ui.updatePatternData = true;
1681 }
1682 
pattOctaDown(bool sampleAllFlag)1683 void pattOctaDown(bool sampleAllFlag)
1684 {
1685 	bool noteDeleted;
1686 	int8_t k;
1687 	note_t *noteSrc;
1688 
1689 	saveUndo();
1690 	for (uint8_t i = 0; i < AMIGA_VOICES; i++)
1691 	{
1692 		for (uint8_t j = 0; j < MOD_ROWS; j++)
1693 		{
1694 			noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
1695 
1696 			if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
1697 				continue;
1698 
1699 			if (noteSrc->period)
1700 			{
1701 				// period -> note
1702 				for (k = 0; k < 36; k++)
1703 				{
1704 					if (noteSrc->period >= periodTable[k])
1705 						break;
1706 				}
1707 
1708 				noteDeleted = false;
1709 				if (k-12 < 0 && config.transDel)
1710 				{
1711 					noteSrc->period = 0;
1712 					noteSrc->sample = 0;
1713 
1714 					noteDeleted = true;
1715 				}
1716 
1717 				if (k >= 12)
1718 					k -= 12;
1719 
1720 				if (!noteDeleted)
1721 					noteSrc->period = periodTable[k];
1722 			}
1723 		}
1724 	}
1725 
1726 	updateWindowTitle(MOD_IS_MODIFIED);
1727 	ui.updatePatternData = true;
1728 }
1729 
keyToNote(SDL_Scancode scancode)1730 int8_t keyToNote(SDL_Scancode scancode)
1731 {
1732 	int8_t note;
1733 	int32_t lookUpKey;
1734 
1735 	if (scancode < SDL_SCANCODE_B || scancode > SDL_SCANCODE_SLASH)
1736 		return -1; // not a note key
1737 
1738 	lookUpKey = (int32_t)scancode - SDL_SCANCODE_B;
1739 	if (lookUpKey < 0 || lookUpKey >= 52)
1740 		return -1; // just in case
1741 
1742 	if (editor.keyOctave == OCTAVE_LOW)
1743 		note = scancode2NoteLo[lookUpKey];
1744 	else
1745 		note = scancode2NoteHi[lookUpKey];
1746 
1747 	return note;
1748 }
1749