1 // for finding memory leaks in debug mode with Visual Studio
2 #if defined _DEBUG && defined _MSC_VER
3 #include <crtdbg.h>
4 #endif
5 
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <stdbool.h>
9 #include <math.h>
10 #include "ft2_header.h"
11 #include "ft2_config.h"
12 #include "ft2_audio.h"
13 #include "ft2_pattern_ed.h"
14 #include "ft2_gui.h"
15 #include "scopes/ft2_scopes.h"
16 #include "ft2_sample_ed.h"
17 #include "ft2_mouse.h"
18 #include "ft2_video.h"
19 #include "ft2_sample_loader.h"
20 #include "ft2_diskop.h"
21 #include "ft2_tables.h"
22 #include "ft2_bmp.h"
23 #include "ft2_structs.h"
24 #include "ft2_bmp.h"
25 
26 #ifdef _MSC_VER
27 #pragma pack(push)
28 #pragma pack(1)
29 #endif
30 typedef struct patHdr_t
31 {
32 	char ID[22], junk1[60];
33 	uint8_t numInstrs, junk2, numChannels;
34 	int16_t waveforms, masterVol;
35 	int32_t dataSize;
36 	char junk4[36];
37 	int16_t junk5;
38 	char instrName[16];
39 	int32_t instrSize;
40 	uint8_t layers;
41 	char junk6[40];
42 	uint8_t junk7, junk8;
43 	int32_t junk9;
44 	uint8_t numSamples;
45 	char junk10[40];
46 }
47 #ifdef __GNUC__
48 __attribute__ ((packed))
49 #endif
50 patHdr_t;
51 
52 typedef struct patWaveHdr_t
53 {
54 	char name[7];
55 	uint8_t fractions;
56 	int32_t sampleLength, loopStart, loopEnd;
57 	uint16_t sampleRate;
58 	int32_t lowFrq, highFreq, rootFrq;
59 	int16_t finetune;
60 	uint8_t panning, envRate[6], envOfs[6], tremSweep, tremRate;
61 	uint8_t tremDepth, vibSweep, vibRate, vibDepth, flags;
62 	int16_t junk1;
63 	uint16_t junk2;
64 	char junk3[36];
65 }
66 #ifdef __GNUC__
67 __attribute__ ((packed))
68 #endif
69 patWaveHdr_t;
70 
71 typedef struct xiHdr_t
72 {
73 	char ID[21], name[23], progName[20];
74 	uint16_t version;
75 	uint8_t note2SampleLUT[96];
76 	int16_t volEnvPoints[12][2], panEnvPoints[12][2];
77 	uint8_t volEnvLength, panEnvLength, volEnvSustain, volEnvLoopStart, volEnvLoopEnd, panEnvSustain, panEnvLoopStart;
78 	uint8_t panEnvLoopEnd, volEnvFlags, panEnvFlags, vibType, vibSweep, vibDepth, vibRate;
79 	uint16_t fadeout;
80 	uint8_t midiOn, midiChannel;
81 	int16_t midiProgram, midiBend;
82 	uint8_t mute, reserved[15];
83 	int16_t numSamples;
84 	xmSmpHdr_t smp[16];
85 }
86 #ifdef __GNUC__
87 __attribute__ ((packed))
88 #endif
89 xiHdr_t;
90 
91 #define PIANOKEY_WHITE_W 10
92 #define PIANOKEY_WHITE_H 46
93 #define PIANOKEY_BLACK_W 7
94 #define PIANOKEY_BLACK_H 29
95 
96 static const bool keyIsBlackTab[12] = { false, true, false, true, false, false, true, false, true, false, true };
97 static const char sharpNote1Char[12] = { 'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B' };
98 static const char sharpNote2Char[12] = { '-', '#', '-', '#', '-', '-', '#', '-', '#', '-', '#', '-' };
99 static const char flatNote1Char[12]  = { 'C', 'D', 'D', 'E', 'E', 'F', 'G', 'G', 'A', 'A', 'B', 'B' };
100 static const char flatNote2Char[12]  = { '-', 'b', '-', 'b', '-', '-', 'b', '-', 'b', '-', 'b', '-' };
101 static const uint8_t whiteKeyIndex[7] = { 0, 2, 4, 5, 7, 9, 11 };
102 static const uint16_t whiteKeysBmpOrder[12] = { 0, 0, 506, 0, 1012, 0, 0, 506, 0, 506, 0, 1012 };
103 static const uint8_t keyDigitXPos[12] = { 11, 16, 22, 27, 33, 44, 49, 55, 60, 66, 71, 77 };
104 static const uint8_t keyXPos[12] = { 8, 15, 19, 26, 30, 41, 48, 52, 59, 63, 70, 74 };
105 static volatile bool updateVolEnv, updatePanEnv;
106 static bool pianoKeyStatus[96];
107 static int32_t lastMouseX, lastMouseY, saveMouseX, saveMouseY;
108 
109 static const uint8_t mx2PianoKey[77] =
110 {
111 	0,0,0,0,0,0,0,1,1,1,1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,
112 	4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,
113 	9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11
114 };
115 
116 // thread data
117 static uint16_t saveInstrNum;
118 static SDL_Thread *thread;
119 
120 extern const uint16_t *note2Period; // ft2_replayer.c
121 
122 void updateInstEditor(void);
123 void updateNewInstrument(void);
124 
sanitizeInstrument(instr_t * ins)125 void sanitizeInstrument(instr_t *ins)
126 {
127 	if (ins == NULL)
128 		return;
129 
130 	ins->midiProgram = CLAMP(ins->midiProgram, 0, 127);
131 	ins->midiBend = CLAMP(ins->midiBend, 0, 36);
132 
133 	if (ins->midiChannel > 15) ins->midiChannel = 15;
134 	if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
135 	if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
136 	if (ins->vibType > 3) ins->vibType = 0;
137 
138 	for (int32_t i = 0; i < 96; i++)
139 	{
140 		if (ins->note2SampleLUT[i] >= MAX_SMP_PER_INST)
141 			ins->note2SampleLUT[i] = MAX_SMP_PER_INST-1;
142 	}
143 
144 	if (ins->volEnvLength > 12) ins->volEnvLength = 12;
145 	if (ins->volEnvLoopStart > 11) ins->volEnvLoopStart = 11;
146 	if (ins->volEnvLoopEnd > 11) ins->volEnvLoopEnd = 11;
147 	if (ins->volEnvSustain > 11) ins->volEnvSustain = 11;
148 	if (ins->panEnvLength > 12) ins->panEnvLength = 12;
149 	if (ins->panEnvLoopStart > 11) ins->panEnvLoopStart = 11;
150 	if (ins->panEnvLoopEnd > 11) ins->panEnvLoopEnd = 11;
151 	if (ins->panEnvSustain > 11) ins->panEnvSustain = 11;
152 
153 	for (int32_t i = 0; i < 12; i++)
154 	{
155 		if ((uint16_t)ins->volEnvPoints[i][0] > 32767) ins->volEnvPoints[i][0] = 32767;
156 		if ((uint16_t)ins->panEnvPoints[i][0] > 32767) ins->panEnvPoints[i][0] = 32767;
157 		if ((uint16_t)ins->volEnvPoints[i][1] > 64) ins->volEnvPoints[i][1] = 64;
158 		if ((uint16_t)ins->panEnvPoints[i][1] > 63) ins->panEnvPoints[i][1] = 63;
159 	}
160 }
161 
getCurDispInstr(void)162 static instr_t *getCurDispInstr(void)
163 {
164 	if (instr[editor.curInstr] == NULL)
165 		return instr[131];
166 
167 	return instr[editor.curInstr];
168 }
169 
copyInstrThread(void * ptr)170 static int32_t SDLCALL copyInstrThread(void *ptr)
171 {
172 	const int16_t dstIns = editor.curInstr;
173 	const int16_t srcIns = editor.srcInstr;
174 
175 	pauseAudio();
176 	freeInstr(dstIns);
177 
178 	bool error = true;
179 	if (instr[srcIns] != NULL)
180 	{
181 		if (allocateInstr(dstIns))
182 		{
183 			int16_t i;
184 
185 			sample_t *srcSmp;
186 			sample_t *dstSmp;
187 
188 			memcpy(instr[dstIns], instr[srcIns], sizeof (instr_t));
189 
190 			// clear all copied sample structs (set up in cloneSample() later)
191 			dstSmp = instr[dstIns]->smp;
192 			for (i = 0; i < MAX_SMP_PER_INST; i++, dstSmp++)
193 				memset(dstSmp, 0, sizeof (sample_t));
194 
195 			// clone all the samples
196 
197 			srcSmp = instr[srcIns]->smp;
198 			dstSmp = instr[dstIns]->smp;
199 
200 			for (i = 0; i < MAX_SMP_PER_INST; i++, srcSmp++, dstSmp++)
201 			{
202 				if (!cloneSample(srcSmp, dstSmp))
203 					break;
204 			}
205 
206 			if (i == MAX_SMP_PER_INST) // did we manage to successfully copy the samples?
207 				error = false; // yes
208 		}
209 	}
210 
211 	resumeAudio();
212 
213 	if (error)
214 		okBoxThreadSafe(0, "System message", "Not enough memory!");
215 
216 	// do not change instrument names!
217 
218 	if (!error)
219 	{
220 		editor.updateCurInstr = true;
221 		setSongModifiedFlag();
222 	}
223 
224 	setMouseBusy(false);
225 
226 	return false;
227 	(void)ptr;
228 }
229 
copyInstr(void)230 void copyInstr(void) // dstInstr = srcInstr
231 {
232 	if (editor.curInstr == 0 || editor.srcInstr == editor.curInstr)
233 		return;
234 
235 	mouseAnimOn();
236 	thread = SDL_CreateThread(copyInstrThread, NULL, NULL);
237 	if (thread == NULL)
238 	{
239 		okBox(0, "System message", "Couldn't create thread!");
240 		return;
241 	}
242 
243 	SDL_DetachThread(thread);
244 }
245 
xchgInstr(void)246 void xchgInstr(void) // dstInstr <-> srcInstr
247 {
248 	if (editor.curInstr == 0 || editor.srcInstr == editor.curInstr)
249 		return;
250 
251 	lockMixerCallback();
252 
253 	// swap instruments
254 	instr_t *dstTmp = instr[editor.curInstr];
255 	instr[editor.curInstr] = instr[editor.srcInstr];
256 	instr[editor.srcInstr] = dstTmp;
257 
258 	// we do not swap instrument names (like FT2)
259 
260 	unlockMixerCallback();
261 
262 	updateNewInstrument();
263 	setSongModifiedFlag();
264 }
265 
drawMIDICh(void)266 static void drawMIDICh(void)
267 {
268 	instr_t *ins = getCurDispInstr();
269 	assert(ins->midiChannel <= 15);
270 	const uint8_t val = ins->midiChannel + 1;
271 	textOutFixed(156, 132, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[val]);
272 }
273 
drawMIDIPrg(void)274 static void drawMIDIPrg(void)
275 {
276 	instr_t *ins = getCurDispInstr();
277 	assert(ins->midiProgram <= 127);
278 	textOutFixed(149, 146, PAL_FORGRND, PAL_DESKTOP, dec3StrTab[ins->midiProgram]);
279 }
280 
drawMIDIBend(void)281 static void drawMIDIBend(void)
282 {
283 	instr_t *ins = getCurDispInstr();
284 	assert(ins->midiBend <= 36);
285 	textOutFixed(156, 160, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->midiBend]);
286 }
287 
midiChDown(void)288 void midiChDown(void)
289 {
290 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
291 		scrollBarScrollLeft(SB_INST_EXT_MIDI_CH, 1);
292 }
293 
midiChUp(void)294 void midiChUp(void)
295 {
296 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
297 		scrollBarScrollRight(SB_INST_EXT_MIDI_CH, 1);
298 }
299 
midiPrgDown(void)300 void midiPrgDown(void)
301 {
302 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
303 		scrollBarScrollLeft(SB_INST_EXT_MIDI_PRG, 1);
304 }
305 
midiPrgUp(void)306 void midiPrgUp(void)
307 {
308 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
309 		scrollBarScrollRight(SB_INST_EXT_MIDI_PRG, 1);
310 }
311 
midiBendDown(void)312 void midiBendDown(void)
313 {
314 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
315 		scrollBarScrollLeft(SB_INST_EXT_MIDI_BEND, 1);
316 }
317 
midiBendUp(void)318 void midiBendUp(void)
319 {
320 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
321 		scrollBarScrollRight(SB_INST_EXT_MIDI_BEND, 1);
322 }
323 
sbMidiChPos(uint32_t pos)324 void sbMidiChPos(uint32_t pos)
325 {
326 	instr_t *ins = instr[editor.curInstr];
327 	if (ins == NULL || editor.curInstr == 0)
328 	{
329 		setScrollBarPos(SB_INST_EXT_MIDI_CH, 0, false);
330 		return;
331 	}
332 
333 	if (ins->midiChannel != (uint8_t)pos)
334 	{
335 		ins->midiChannel = (uint8_t)pos;
336 		drawMIDICh();
337 		setSongModifiedFlag();
338 	}
339 }
340 
sbMidiPrgPos(uint32_t pos)341 void sbMidiPrgPos(uint32_t pos)
342 {
343 	instr_t *ins = instr[editor.curInstr];
344 	if (ins == NULL || editor.curInstr == 0)
345 	{
346 		setScrollBarPos(SB_INST_EXT_MIDI_PRG, 0, false);
347 		return;
348 	}
349 
350 	if (ins->midiProgram != (int16_t)pos)
351 	{
352 		ins->midiProgram = (int16_t)pos;
353 		drawMIDIPrg();
354 		setSongModifiedFlag();
355 	}
356 }
357 
sbMidiBendPos(uint32_t pos)358 void sbMidiBendPos(uint32_t pos)
359 {
360 	instr_t *ins = instr[editor.curInstr];
361 	if (ins == NULL || editor.curInstr == 0)
362 	{
363 		setScrollBarPos(SB_INST_EXT_MIDI_BEND, 0, false);
364 		return;
365 	}
366 
367 	if (ins->midiBend != (int16_t)pos)
368 	{
369 		ins->midiBend = (int16_t)pos;
370 		drawMIDIBend();
371 		setSongModifiedFlag();
372 	}
373 }
374 
updateNewSample(void)375 void updateNewSample(void)
376 {
377 	if (ui.instrSwitcherShown)
378 		updateInstrumentSwitcher();
379 
380 	updateSampleEditorSample();
381 
382 	if (ui.sampleEditorShown)
383 		updateSampleEditor();
384 
385 	if (ui.instEditorShown || ui.instEditorExtShown)
386 		updateInstEditor();
387 }
388 
updateNewInstrument(void)389 void updateNewInstrument(void)
390 {
391 	updateTextBoxPointers();
392 
393 	if (ui.instrSwitcherShown)
394 		updateInstrumentSwitcher();
395 
396 	editor.currVolEnvPoint = 0;
397 	editor.currPanEnvPoint = 0;
398 
399 	updateSampleEditorSample();
400 
401 	if (ui.sampleEditorShown)
402 		updateSampleEditor();
403 
404 	if (ui.instEditorShown || ui.instEditorExtShown)
405 		updateInstEditor();
406 
407 	if (ui.advEditShown)
408 		updateAdvEdit();
409 }
410 
drawVolEnvSus(void)411 static void drawVolEnvSus(void)
412 {
413 	instr_t *ins = getCurDispInstr();
414 	assert(ins->volEnvSustain < 100);
415 	textOutFixed(382, 206, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->volEnvSustain]);
416 }
417 
drawVolEnvRepS(void)418 static void drawVolEnvRepS(void)
419 {
420 	instr_t *ins = getCurDispInstr();
421 	assert(ins->volEnvLoopStart < 100);
422 	textOutFixed(382, 233, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->volEnvLoopStart]);
423 }
424 
drawVolEnvRepE(void)425 static void drawVolEnvRepE(void)
426 {
427 	instr_t *ins = getCurDispInstr();
428 	assert(ins->volEnvLoopEnd < 100);
429 	textOutFixed(382, 247, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->volEnvLoopEnd]);
430 }
431 
drawPanEnvSus(void)432 static void drawPanEnvSus(void)
433 {
434 	instr_t *ins = getCurDispInstr();
435 	assert(ins->panEnvSustain < 100);
436 	textOutFixed(382, 293, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->panEnvSustain]);
437 }
438 
drawPanEnvRepS(void)439 static void drawPanEnvRepS(void)
440 {
441 	instr_t *ins = getCurDispInstr();
442 	assert(ins->panEnvLoopStart < 100);
443 	textOutFixed(382, 320, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->panEnvLoopStart]);
444 }
445 
drawPanEnvRepE(void)446 static void drawPanEnvRepE(void)
447 {
448 	instr_t *ins = getCurDispInstr();
449 	assert(ins->panEnvLoopEnd < 100);
450 	textOutFixed(382, 334, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->panEnvLoopEnd]);
451 }
452 
drawVolume(void)453 static void drawVolume(void)
454 {
455 	sample_t *s;
456 	if (instr[editor.curInstr] == NULL)
457 		s = &instr[0]->smp[0];
458 	else
459 		s = &instr[editor.curInstr]->smp[editor.curSmp];
460 
461 	hexOutBg(505, 177, PAL_FORGRND, PAL_DESKTOP, s->volume, 2);
462 }
463 
drawPanning(void)464 static void drawPanning(void)
465 {
466 	sample_t *s;
467 	if (instr[editor.curInstr] == NULL)
468 		s = &instr[0]->smp[0];
469 	else
470 		s = &instr[editor.curInstr]->smp[editor.curSmp];
471 
472 	hexOutBg(505, 191, PAL_FORGRND, PAL_DESKTOP, s->panning, 2);
473 }
474 
drawC4Rate(void)475 void drawC4Rate(void)
476 {
477 	fillRect(465, 299, 71, 8, PAL_DESKTOP);
478 
479 	int32_t C4Hz = 0;
480 	if (editor.curInstr != 0)
481 	{
482 		instr_t *ins = instr[editor.curInstr];
483 		if (ins != NULL)
484 			C4Hz = (int32_t)(getSampleC4Rate(&ins->smp[editor.curSmp]) + 0.5); // rounded
485 	}
486 
487 	char str[64];
488 	sprintf(str, "%dHz", C4Hz);
489 	textOut(465, 299, PAL_FORGRND, str);
490 }
491 
drawFineTune(void)492 static void drawFineTune(void)
493 {
494 	sample_t *s;
495 	if (instr[editor.curInstr] == NULL)
496 		s = &instr[0]->smp[0];
497 	else
498 		s = &instr[editor.curInstr]->smp[editor.curSmp];
499 
500 	fillRect(491, 205, 27, 8, PAL_DESKTOP);
501 
502 	int16_t  ftune = s->finetune;
503 	if (ftune == 0)
504 	{
505 		charOut(512, 205, PAL_FORGRND, '0');
506 		return;
507 	}
508 
509 	const char sign = (ftune < 0) ? '-' : '+';
510 
511 	ftune = ABS(ftune);
512 	if (ftune >= 100)
513 	{
514 		charOut(491, 205, PAL_FORGRND, sign);
515 		charOut(498 + (0 * 7), 205, PAL_FORGRND, '0' + ((ftune / 100) % 10));
516 		charOut(498 + (1 * 7), 205, PAL_FORGRND, '0' + ((ftune /  10) % 10));
517 		charOut(498 + (2 * 7), 205, PAL_FORGRND, '0' + (ftune % 10));
518 	}
519 	else if (ftune >= 10)
520 	{
521 		charOut(498, 205, PAL_FORGRND, sign);
522 		charOut(505 + (0 * 7), 205, PAL_FORGRND, '0' + ((ftune / 10) % 10));
523 		charOut(505 + (1 * 7), 205, PAL_FORGRND, '0' + (ftune % 10));
524 	}
525 	else
526 	{
527 		charOut(505, 205, PAL_FORGRND, sign);
528 		charOut(512, 205, PAL_FORGRND, '0' + (ftune % 10));
529 	}
530 }
531 
drawFadeout(void)532 static void drawFadeout(void)
533 {
534 	hexOutBg(498, 222, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->fadeout, 3);
535 }
536 
drawVibSpeed(void)537 static void drawVibSpeed(void)
538 {
539 	hexOutBg(505, 236, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->vibRate, 2);
540 }
541 
drawVibDepth(void)542 static void drawVibDepth(void)
543 {
544 	hexOutBg(512, 250, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->vibDepth, 1);
545 }
546 
drawVibSweep(void)547 static void drawVibSweep(void)
548 {
549 	hexOutBg(505, 264, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->vibSweep, 2);
550 }
551 
drawRelativeNote(void)552 static void drawRelativeNote(void)
553 {
554 	char noteChar1, noteChar2;
555 	int8_t note2;
556 
557 	if (instr[editor.curInstr] == NULL)
558 	{
559 		fillRect(600, 299, 8*3, 8, PAL_BCKGRND);
560 		return;
561 	}
562 
563 	if (editor.curInstr == 0)
564 		note2 = 48;
565 	else
566 		note2 = 48 + instr[editor.curInstr]->smp[editor.curSmp].relativeNote;
567 
568 	const int8_t note = note2 % 12;
569 	if (config.ptnAcc == 0)
570 	{
571 		noteChar1 = sharpNote1Char[note];
572 		noteChar2 = sharpNote2Char[note];
573 	}
574 	else
575 	{
576 		noteChar1 = flatNote1Char[note];
577 		noteChar2 = flatNote2Char[note];
578 	}
579 
580 	const char octaChar = '0' + (note2 / 12);
581 
582 	charOutBg(600, 299, PAL_FORGRND, PAL_BCKGRND, noteChar1);
583 	charOutBg(608, 299, PAL_FORGRND, PAL_BCKGRND, noteChar2);
584 	charOutBg(616, 299, PAL_FORGRND, PAL_BCKGRND, octaChar);
585 }
586 
setStdVolEnvelope(instr_t * ins,uint8_t num)587 static void setStdVolEnvelope(instr_t *ins, uint8_t num)
588 {
589 	if (editor.curInstr == 0 || ins == NULL)
590 		return;
591 
592 	pauseMusic();
593 
594 	ins->fadeout = config.stdFadeout[num];
595 	ins->volEnvSustain = (uint8_t)config.stdVolEnvSustain[num];
596 	ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[num];
597 	ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[num];
598 	ins->volEnvLength = (uint8_t)config.stdVolEnvLength[num];
599 	ins->volEnvFlags = (uint8_t)config.stdVolEnvFlags[num];
600 	ins->vibRate = (uint8_t)config.stdVibRate[num];
601 	ins->vibDepth = (uint8_t)config.stdVibDepth[num];
602 	ins->vibSweep = (uint8_t)config.stdVibSweep[num];
603 	ins->vibType = (uint8_t)config.stdVibType[num];
604 
605 	memcpy(ins->volEnvPoints, config.stdEnvPoints[num][0], sizeof (int16_t) * 12 * 2);
606 
607 	resumeMusic();
608 }
609 
setStdPanEnvelope(instr_t * ins,uint8_t num)610 static void setStdPanEnvelope(instr_t *ins, uint8_t num)
611 {
612 	if (editor.curInstr == 0 || ins == NULL)
613 		return;
614 
615 	pauseMusic();
616 
617 	ins->panEnvLength = (uint8_t)config.stdPanEnvLength[num];
618 	ins->panEnvSustain = (uint8_t)config.stdPanEnvSustain[num];
619 	ins->panEnvLoopStart = (uint8_t)config.stdPanEnvLoopStart[num];
620 	ins->panEnvLoopEnd = (uint8_t)config.stdPanEnvLoopEnd[num];
621 	ins->panEnvFlags = (uint8_t)config.stdPanEnvFlags[num];
622 
623 	memcpy(ins->panEnvPoints, config.stdEnvPoints[num][1], sizeof (int16_t) * 12 * 2);
624 
625 	resumeMusic();
626 }
627 
setOrStoreVolEnvPreset(uint8_t num)628 static void setOrStoreVolEnvPreset(uint8_t num)
629 {
630 	instr_t *ins = instr[editor.curInstr];
631 	if (ins == NULL || editor.curInstr == 0)
632 		return;
633 
634 	if (mouse.rightButtonReleased)
635 	{
636 		// store preset
637 		config.stdFadeout[num] = ins->fadeout;
638 		config.stdVolEnvSustain[num] = ins->volEnvSustain;
639 		config.stdVolEnvLoopStart[num] = ins->volEnvLoopStart;
640 		config.stdVolEnvLoopEnd[num] = ins->volEnvLoopEnd;
641 		config.stdVolEnvLength[num] = ins->volEnvLength;
642 		config.stdVolEnvFlags[num] = ins->volEnvFlags;
643 		config.stdVibRate[num] = ins->vibRate;
644 		config.stdVibDepth[num] = ins->vibDepth;
645 		config.stdVibSweep[num] = ins->vibSweep;
646 		config.stdVibType[num] = ins->vibType;
647 
648 		memcpy(config.stdEnvPoints[num][0], ins->volEnvPoints, sizeof (int16_t) * 12 * 2);
649 	}
650 	else if (mouse.leftButtonReleased)
651 	{
652 		// read preset
653 		setStdVolEnvelope(ins, num);
654 		editor.currVolEnvPoint = 0;
655 		updateInstEditor();
656 		setSongModifiedFlag();
657 	}
658 }
659 
setOrStorePanEnvPreset(uint8_t num)660 static void setOrStorePanEnvPreset(uint8_t num)
661 {
662 	instr_t *ins = instr[editor.curInstr];
663 	if (ins == NULL || editor.curInstr == 0)
664 		return;
665 
666 	if (mouse.rightButtonReleased)
667 	{
668 		// store preset
669 		config.stdFadeout[num] = ins->fadeout;
670 		config.stdPanEnvSustain[num] = ins->panEnvSustain;
671 		config.stdPanEnvLoopStart[num] = ins->panEnvLoopStart;
672 		config.stdPanEnvLoopEnd[num] = ins->panEnvLoopEnd;
673 		config.stdPanEnvLength[num] = ins->panEnvLength;
674 		config.stdPanEnvFlags[num] = ins->panEnvFlags;
675 		config.stdVibRate[num] = ins->vibRate;
676 		config.stdVibDepth[num] = ins->vibDepth;
677 		config.stdVibSweep[num] = ins->vibSweep;
678 		config.stdVibType[num] = ins->vibType;
679 
680 		memcpy(config.stdEnvPoints[num][1], ins->panEnvPoints, sizeof (int16_t) * 12 * 2);
681 	}
682 	else if (mouse.leftButtonReleased)
683 	{
684 		// read preset
685 		setStdPanEnvelope(ins, num);
686 		editor.currPanEnvPoint = 0;
687 		updateInstEditor();
688 		setSongModifiedFlag();
689 	}
690 }
691 
volPreDef1(void)692 void volPreDef1(void)
693 {
694 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
695 		setOrStoreVolEnvPreset(1 - 1);
696 }
697 
volPreDef2(void)698 void volPreDef2(void)
699 {
700 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
701 		setOrStoreVolEnvPreset(2 - 1);
702 }
703 
volPreDef3(void)704 void volPreDef3(void)
705 {
706 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
707 		setOrStoreVolEnvPreset(3 - 1);
708 }
709 
volPreDef4(void)710 void volPreDef4(void)
711 {
712 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
713 		setOrStoreVolEnvPreset(4 - 1);
714 }
715 
volPreDef5(void)716 void volPreDef5(void)
717 {
718 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
719 		setOrStoreVolEnvPreset(5 - 1);
720 }
721 
volPreDef6(void)722 void volPreDef6(void)
723 {
724 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
725 		setOrStoreVolEnvPreset(6 - 1);
726 }
727 
panPreDef1(void)728 void panPreDef1(void)
729 {
730 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
731 		setOrStorePanEnvPreset(1 - 1);
732 }
733 
panPreDef2(void)734 void panPreDef2(void)
735 {
736 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
737 		setOrStorePanEnvPreset(2 - 1);
738 }
739 
panPreDef3(void)740 void panPreDef3(void)
741 {
742 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
743 		setOrStorePanEnvPreset(3 - 1);
744 }
745 
panPreDef4(void)746 void panPreDef4(void)
747 {
748 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
749 		setOrStorePanEnvPreset(4 - 1);
750 }
751 
panPreDef5(void)752 void panPreDef5(void)
753 {
754 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
755 		setOrStorePanEnvPreset(5 - 1);
756 }
757 
panPreDef6(void)758 void panPreDef6(void)
759 {
760 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
761 		setOrStorePanEnvPreset(6 - 1);
762 }
763 
relativeNoteOctUp(void)764 void relativeNoteOctUp(void)
765 {
766 	sample_t *s;
767 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
768 		return;
769 
770 	s = &instr[editor.curInstr]->smp[editor.curSmp];
771 	if (s->relativeNote <= 71-12)
772 		s->relativeNote += 12;
773 	else
774 		s->relativeNote = 71;
775 
776 	drawRelativeNote();
777 	drawC4Rate();
778 	setSongModifiedFlag();
779 }
780 
relativeNoteOctDown(void)781 void relativeNoteOctDown(void)
782 {
783 	sample_t *s;
784 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
785 		return;
786 
787 	s = &instr[editor.curInstr]->smp[editor.curSmp];
788 	if (s->relativeNote >= -48+12)
789 		s->relativeNote -= 12;
790 	else
791 		s->relativeNote = -48;
792 
793 	drawRelativeNote();
794 	drawC4Rate();
795 	setSongModifiedFlag();
796 }
797 
relativeNoteUp(void)798 void relativeNoteUp(void)
799 {
800 	sample_t *s;
801 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
802 		return;
803 
804 	s = &instr[editor.curInstr]->smp[editor.curSmp];
805 	if (s->relativeNote < 71)
806 	{
807 		s->relativeNote++;
808 		drawRelativeNote();
809 		drawC4Rate();
810 		setSongModifiedFlag();
811 	}
812 }
813 
relativeNoteDown(void)814 void relativeNoteDown(void)
815 {
816 	sample_t *s;
817 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
818 		return;
819 
820 	s = &instr[editor.curInstr]->smp[editor.curSmp];
821 	if (s->relativeNote > -48)
822 	{
823 		s->relativeNote--;
824 		drawRelativeNote();
825 		drawC4Rate();
826 		setSongModifiedFlag();
827 	}
828 }
829 
volEnvAdd(void)830 void volEnvAdd(void)
831 {
832 	instr_t *ins = instr[editor.curInstr];
833 	if (editor.curInstr == 0 || ins == NULL)
834 		return;
835 
836 	const int16_t ant = ins->volEnvLength;
837 	if (ant >= 12)
838 		return;
839 
840 	int16_t i = (int16_t)editor.currVolEnvPoint;
841 	if (i < 0 || i >= ant)
842 	{
843 		i = ant-1;
844 		if (i < 0)
845 			i = 0;
846 	}
847 
848 	if (i < ant-1 && ins->volEnvPoints[i+1][0]-ins->volEnvPoints[i][0] < 2)
849 		return;
850 
851 	if (ins->volEnvPoints[i][0] >= 323)
852 		return;
853 
854 	for (int16_t j = ant; j > i; j--)
855 	{
856 		ins->volEnvPoints[j][0] = ins->volEnvPoints[j-1][0];
857 		ins->volEnvPoints[j][1] = ins->volEnvPoints[j-1][1];
858 	}
859 
860 	if (ins->volEnvSustain > i) { ins->volEnvSustain++; drawVolEnvSus();  }
861 	if (ins->volEnvLoopStart > i) { ins->volEnvLoopStart++; drawVolEnvRepS(); }
862 	if (ins->volEnvLoopEnd > i) { ins->volEnvLoopEnd++; drawVolEnvRepE(); }
863 
864 	if (i < ant-1)
865 	{
866 		ins->volEnvPoints[i+1][0] = (ins->volEnvPoints[i][0] + ins->volEnvPoints[i+2][0]) / 2;
867 		ins->volEnvPoints[i+1][1] = (ins->volEnvPoints[i][1] + ins->volEnvPoints[i+2][1]) / 2;
868 	}
869 	else
870 	{
871 		ins->volEnvPoints[i+1][0] = ins->volEnvPoints[i][0] + 10;
872 		ins->volEnvPoints[i+1][1] = ins->volEnvPoints[i][1];
873 	}
874 
875 	if (ins->volEnvPoints[i+1][0] > 324)
876 		ins->volEnvPoints[i+1][0] = 324;
877 
878 	ins->volEnvLength++;
879 
880 	updateVolEnv = true;
881 	setSongModifiedFlag();
882 }
883 
volEnvDel(void)884 void volEnvDel(void)
885 {
886 	instr_t *ins = instr[editor.curInstr];
887 	if (ins == NULL || editor.curInstr == 0 || ins->volEnvLength <= 2)
888 		return;
889 
890 	int16_t i = (int16_t)editor.currVolEnvPoint;
891 	if (i < 0 || i >= ins->volEnvLength)
892 		return;
893 
894 	for (int16_t j = i; j < ins->volEnvLength; j++)
895 	{
896 		ins->volEnvPoints[j][0] = ins->volEnvPoints[j+1][0];
897 		ins->volEnvPoints[j][1] = ins->volEnvPoints[j+1][1];
898 	}
899 
900 	bool drawSust = false;
901 	bool drawRepS = false;
902 	bool drawRepE = false;
903 
904 	if (ins->volEnvSustain > i) { ins->volEnvSustain--; drawSust = true; }
905 	if (ins->volEnvLoopStart > i) { ins->volEnvLoopStart--; drawRepS = true; }
906 	if (ins->volEnvLoopEnd > i) { ins->volEnvLoopEnd--; drawRepE = true; }
907 
908 	ins->volEnvPoints[0][0] = 0;
909 	ins->volEnvLength--;
910 
911 	if (ins->volEnvSustain >= ins->volEnvLength) { ins->volEnvSustain = ins->volEnvLength - 1; drawSust = true; }
912 	if (ins->volEnvLoopStart >= ins->volEnvLength) { ins->volEnvLoopStart = ins->volEnvLength - 1; drawRepS = true; }
913 	if (ins->volEnvLoopEnd >= ins->volEnvLength) { ins->volEnvLoopEnd = ins->volEnvLength - 1; drawRepE = true; }
914 
915 	if (drawSust) drawVolEnvSus();
916 	if (drawRepS) drawVolEnvRepS();
917 	if (drawRepE) drawVolEnvRepE();
918 
919 	if (ins->volEnvLength == 0)
920 		editor.currVolEnvPoint = 0;
921 	else if (editor.currVolEnvPoint >= ins->volEnvLength)
922 		editor.currVolEnvPoint = ins->volEnvLength-1;
923 
924 	updateVolEnv = true;
925 	setSongModifiedFlag();
926 }
927 
volEnvSusUp(void)928 void volEnvSusUp(void)
929 {
930 	instr_t *ins = instr[editor.curInstr];
931 	if (ins == NULL || editor.curInstr == 0)
932 		return;
933 
934 	if (ins->volEnvSustain < ins->volEnvLength-1)
935 	{
936 		ins->volEnvSustain++;
937 		drawVolEnvSus();
938 		updateVolEnv = true;
939 		setSongModifiedFlag();
940 	}
941 }
942 
volEnvSusDown(void)943 void volEnvSusDown(void)
944 {
945 	instr_t *ins = instr[editor.curInstr];
946 	if (ins == NULL || editor.curInstr == 0)
947 		return;
948 
949 	if (ins->volEnvSustain > 0)
950 	{
951 		ins->volEnvSustain--;
952 		drawVolEnvSus();
953 		updateVolEnv = true;
954 		setSongModifiedFlag();
955 	}
956 }
957 
volEnvRepSUp(void)958 void volEnvRepSUp(void)
959 {
960 	instr_t *ins = instr[editor.curInstr];
961 	if (ins == NULL || editor.curInstr == 0)
962 		return;
963 
964 	if (ins->volEnvLoopStart < ins->volEnvLoopEnd)
965 	{
966 		ins->volEnvLoopStart++;
967 		drawVolEnvRepS();
968 		updateVolEnv = true;
969 		setSongModifiedFlag();
970 	}
971 }
972 
volEnvRepSDown(void)973 void volEnvRepSDown(void)
974 {
975 	instr_t *ins = instr[editor.curInstr];
976 	if (ins == NULL || editor.curInstr == 0)
977 		return;
978 
979 	if (ins->volEnvLoopStart > 0)
980 	{
981 		ins->volEnvLoopStart--;
982 		drawVolEnvRepS();
983 		updateVolEnv = true;
984 		setSongModifiedFlag();
985 	}
986 }
987 
volEnvRepEUp(void)988 void volEnvRepEUp(void)
989 {
990 	instr_t *ins = instr[editor.curInstr];
991 	if (ins == NULL || editor.curInstr == 0)
992 		return;
993 
994 	if (ins->volEnvLoopEnd < ins->volEnvLength-1)
995 	{
996 		ins->volEnvLoopEnd++;
997 		drawVolEnvRepE();
998 		updateVolEnv = true;
999 		setSongModifiedFlag();
1000 	}
1001 }
1002 
volEnvRepEDown(void)1003 void volEnvRepEDown(void)
1004 {
1005 	instr_t *ins = instr[editor.curInstr];
1006 	if (ins == NULL || editor.curInstr == 0)
1007 		return;
1008 
1009 	if (ins->volEnvLoopEnd > ins->volEnvLoopStart)
1010 	{
1011 		ins->volEnvLoopEnd--;
1012 		drawVolEnvRepE();
1013 		updateVolEnv = true;
1014 		setSongModifiedFlag();
1015 	}
1016 }
1017 
panEnvAdd(void)1018 void panEnvAdd(void)
1019 {
1020 	instr_t *ins = instr[editor.curInstr];
1021 	if (ins == NULL || editor.curInstr == 0)
1022 		return;
1023 
1024 	const int16_t ant = ins->panEnvLength;
1025 	if (ant >= 12)
1026 		return;
1027 
1028 	int16_t i = (int16_t)editor.currPanEnvPoint;
1029 	if (i < 0 || i >= ant)
1030 	{
1031 		i = ant-1;
1032 		if (i < 0)
1033 			i = 0;
1034 	}
1035 
1036 	if (i < ant-1 && ins->panEnvPoints[i+1][0]-ins->panEnvPoints[i][0] < 2)
1037 		return;
1038 
1039 	if (ins->panEnvPoints[i][0] >= 323)
1040 		return;
1041 
1042 	for (int16_t j = ant; j > i; j--)
1043 	{
1044 		ins->panEnvPoints[j][0] = ins->panEnvPoints[j-1][0];
1045 		ins->panEnvPoints[j][1] = ins->panEnvPoints[j-1][1];
1046 	}
1047 
1048 	if (ins->panEnvSustain > i) { ins->panEnvSustain++; drawPanEnvSus();  }
1049 	if (ins->panEnvLoopStart > i) { ins->panEnvLoopStart++; drawPanEnvRepS(); }
1050 	if (ins->panEnvLoopEnd > i) { ins->panEnvLoopEnd++; drawPanEnvRepE(); }
1051 
1052 	if (i < ant-1)
1053 	{
1054 		ins->panEnvPoints[i+1][0] = (ins->panEnvPoints[i][0] + ins->panEnvPoints[i+2][0]) / 2;
1055 		ins->panEnvPoints[i+1][1] = (ins->panEnvPoints[i][1] + ins->panEnvPoints[i+2][1]) / 2;
1056 	}
1057 	else
1058 	{
1059 		ins->panEnvPoints[i+1][0] = ins->panEnvPoints[i][0] + 10;
1060 		ins->panEnvPoints[i+1][1] = ins->panEnvPoints[i][1];
1061 	}
1062 
1063 	if (ins->panEnvPoints[i+1][0] > 324)
1064 		ins->panEnvPoints[i+1][0] = 324;
1065 
1066 	ins->panEnvLength++;
1067 
1068 	updatePanEnv = true;
1069 	setSongModifiedFlag();
1070 }
1071 
panEnvDel(void)1072 void panEnvDel(void)
1073 {
1074 	instr_t *ins = instr[editor.curInstr];
1075 	if (ins == NULL || editor.curInstr == 0 || ins->panEnvLength <= 2)
1076 		return;
1077 
1078 	int16_t i = (int16_t)editor.currPanEnvPoint;
1079 	if (i < 0 || i >= ins->panEnvLength)
1080 		return;
1081 
1082 	for (int16_t j = i; j < ins->panEnvLength; j++)
1083 	{
1084 		ins->panEnvPoints[j][0] = ins->panEnvPoints[j+1][0];
1085 		ins->panEnvPoints[j][1] = ins->panEnvPoints[j+1][1];
1086 	}
1087 
1088 	bool drawSust = false;
1089 	bool drawRepS = false;
1090 	bool drawRepE = false;
1091 
1092 	if (ins->panEnvSustain > i) { ins->panEnvSustain--; drawSust = true; }
1093 	if (ins->panEnvLoopStart > i) { ins->panEnvLoopStart--; drawRepS = true; }
1094 	if (ins->panEnvLoopEnd > i) { ins->panEnvLoopEnd--; drawRepE = true; }
1095 
1096 	ins->panEnvPoints[0][0] = 0;
1097 	ins->panEnvLength--;
1098 
1099 	if (ins->panEnvSustain >= ins->panEnvLength) { ins->panEnvSustain = ins->panEnvLength - 1; drawSust = true; }
1100 	if (ins->panEnvLoopStart >= ins->panEnvLength) { ins->panEnvLoopStart = ins->panEnvLength - 1; drawRepS = true; }
1101 	if (ins->panEnvLoopEnd >= ins->panEnvLength) { ins->panEnvLoopEnd = ins->panEnvLength - 1; drawRepE = true; }
1102 
1103 	if (drawSust) drawPanEnvSus();
1104 	if (drawRepS) drawPanEnvRepS();
1105 	if (drawRepE) drawPanEnvRepE();
1106 
1107 	if (ins->panEnvLength == 0)
1108 		editor.currPanEnvPoint = 0;
1109 	else if (editor.currPanEnvPoint >= ins->panEnvLength)
1110 		editor.currPanEnvPoint = ins->panEnvLength-1;
1111 
1112 	updatePanEnv = true;
1113 	setSongModifiedFlag();
1114 }
1115 
panEnvSusUp(void)1116 void panEnvSusUp(void)
1117 {
1118 	instr_t *ins = instr[editor.curInstr];
1119 	if (ins == NULL || editor.curInstr == 0)
1120 		return;
1121 
1122 	if (ins->panEnvSustain < ins->panEnvLength-1)
1123 	{
1124 		ins->panEnvSustain++;
1125 		drawPanEnvSus();
1126 		updatePanEnv = true;
1127 		setSongModifiedFlag();
1128 	}
1129 }
1130 
panEnvSusDown(void)1131 void panEnvSusDown(void)
1132 {
1133 	instr_t *ins = instr[editor.curInstr];
1134 	if (ins == NULL || editor.curInstr == 0)
1135 		return;
1136 
1137 	if (ins->panEnvSustain > 0)
1138 	{
1139 		ins->panEnvSustain--;
1140 		drawPanEnvSus();
1141 		updatePanEnv = true;
1142 		setSongModifiedFlag();
1143 	}
1144 }
1145 
panEnvRepSUp(void)1146 void panEnvRepSUp(void)
1147 {
1148 	instr_t *ins = instr[editor.curInstr];
1149 	if (ins == NULL || editor.curInstr == 0)
1150 		return;
1151 
1152 	if (ins->panEnvLoopStart < ins->panEnvLoopEnd)
1153 	{
1154 		ins->panEnvLoopStart++;
1155 		drawPanEnvRepS();
1156 		updatePanEnv = true;
1157 		setSongModifiedFlag();
1158 	}
1159 }
1160 
panEnvRepSDown(void)1161 void panEnvRepSDown(void)
1162 {
1163 	instr_t *ins = instr[editor.curInstr];
1164 	if (ins == NULL || editor.curInstr == 0)
1165 		return;
1166 
1167 	if (ins->panEnvLoopStart > 0)
1168 	{
1169 		ins->panEnvLoopStart--;
1170 		drawPanEnvRepS();
1171 		updatePanEnv = true;
1172 		setSongModifiedFlag();
1173 	}
1174 }
1175 
panEnvRepEUp(void)1176 void panEnvRepEUp(void)
1177 {
1178 	instr_t *ins = instr[editor.curInstr];
1179 	if (ins == NULL || editor.curInstr == 0)
1180 		return;
1181 
1182 	if (ins->panEnvLoopEnd < ins->panEnvLength-1)
1183 	{
1184 		ins->panEnvLoopEnd++;
1185 		drawPanEnvRepE();
1186 		updatePanEnv = true;
1187 		setSongModifiedFlag();
1188 	}
1189 }
1190 
panEnvRepEDown(void)1191 void panEnvRepEDown(void)
1192 {
1193 	instr_t *ins = instr[editor.curInstr];
1194 	if (ins == NULL || editor.curInstr == 0)
1195 		return;
1196 
1197 	if (ins->panEnvLoopEnd > ins->panEnvLoopStart)
1198 	{
1199 		ins->panEnvLoopEnd--;
1200 		drawPanEnvRepE();
1201 		updatePanEnv = true;
1202 		setSongModifiedFlag();
1203 	}
1204 }
1205 
volDown(void)1206 void volDown(void)
1207 {
1208 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1209 		scrollBarScrollLeft(SB_INST_VOL, 1);
1210 }
1211 
volUp(void)1212 void volUp(void)
1213 {
1214 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1215 		scrollBarScrollRight(SB_INST_VOL, 1);
1216 }
1217 
panDown(void)1218 void panDown(void)
1219 {
1220 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1221 		scrollBarScrollLeft(SB_INST_PAN, 1);
1222 }
1223 
panUp(void)1224 void panUp(void)
1225 {
1226 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1227 		scrollBarScrollRight(SB_INST_PAN, 1);
1228 }
1229 
ftuneDown(void)1230 void ftuneDown(void)
1231 {
1232 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1233 		scrollBarScrollLeft(SB_INST_FTUNE, 1);
1234 }
1235 
ftuneUp(void)1236 void ftuneUp(void)
1237 {
1238 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1239 		scrollBarScrollRight(SB_INST_FTUNE, 1);
1240 }
1241 
fadeoutDown(void)1242 void fadeoutDown(void)
1243 {
1244 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1245 		scrollBarScrollLeft(SB_INST_FADEOUT, 1);
1246 }
1247 
fadeoutUp(void)1248 void fadeoutUp(void)
1249 {
1250 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1251 		scrollBarScrollRight(SB_INST_FADEOUT, 1);
1252 }
1253 
vibSpeedDown(void)1254 void vibSpeedDown(void)
1255 {
1256 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1257 		scrollBarScrollLeft(SB_INST_VIBSPEED, 1);
1258 }
1259 
vibSpeedUp(void)1260 void vibSpeedUp(void)
1261 {
1262 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1263 		scrollBarScrollRight(SB_INST_VIBSPEED, 1);
1264 }
1265 
vibDepthDown(void)1266 void vibDepthDown(void)
1267 {
1268 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1269 		scrollBarScrollLeft(SB_INST_VIBDEPTH, 1);
1270 }
1271 
vibDepthUp(void)1272 void vibDepthUp(void)
1273 {
1274 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1275 		scrollBarScrollRight(SB_INST_VIBDEPTH, 1);
1276 }
1277 
vibSweepDown(void)1278 void vibSweepDown(void)
1279 {
1280 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1281 		scrollBarScrollLeft(SB_INST_VIBSWEEP, 1);
1282 }
1283 
vibSweepUp(void)1284 void vibSweepUp(void)
1285 {
1286 	if (editor.curInstr != 0 && instr[editor.curInstr] != NULL)
1287 		scrollBarScrollRight(SB_INST_VIBSWEEP, 1);
1288 }
1289 
setVolumeScroll(uint32_t pos)1290 void setVolumeScroll(uint32_t pos)
1291 {
1292 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1293 	{
1294 		if (editor.curInstr == 0 && editor.curSmp != 0)
1295 			setScrollBarPos(SB_INST_VOL, 0x40, false);
1296 		else
1297 			setScrollBarPos(SB_INST_VOL, 0, false);
1298 
1299 		return;
1300 	}
1301 
1302 	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
1303 	if (s->volume != (uint8_t)pos)
1304 	{
1305 		s->volume = (uint8_t)pos;
1306 		drawVolume();
1307 		setSongModifiedFlag();
1308 	}
1309 }
1310 
setPanningScroll(uint32_t pos)1311 void setPanningScroll(uint32_t pos)
1312 {
1313 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1314 	{
1315 		setScrollBarPos(SB_INST_PAN, 0x80, false);
1316 		return;
1317 	}
1318 
1319 	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
1320 	if (s->panning != (uint8_t)pos)
1321 	{
1322 		s->panning = (uint8_t)pos;
1323 		drawPanning();
1324 		setSongModifiedFlag();
1325 	}
1326 }
1327 
setFinetuneScroll(uint32_t pos)1328 void setFinetuneScroll(uint32_t pos)
1329 {
1330 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1331 	{
1332 		setScrollBarPos(SB_INST_FTUNE, 128, false); // finetune 0
1333 		return;
1334 	}
1335 
1336 	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
1337 	if (s->finetune != (int8_t)(pos - 128))
1338 	{
1339 		s->finetune = (int8_t)(pos - 128);
1340 		drawFineTune();
1341 		drawC4Rate();
1342 		setSongModifiedFlag();
1343 	}
1344 }
1345 
setFadeoutScroll(uint32_t pos)1346 void setFadeoutScroll(uint32_t pos)
1347 {
1348 	instr_t *ins = instr[editor.curInstr];
1349 	if (ins == NULL)
1350 	{
1351 		setScrollBarPos(SB_INST_FADEOUT, 0, false);
1352 		return;
1353 	}
1354 
1355 	if (editor.curInstr == 0)
1356 	{
1357 		setScrollBarPos(SB_INST_FADEOUT, 0x80, false);
1358 		return;
1359 	}
1360 
1361 	if (ins->fadeout != (uint16_t)pos)
1362 	{
1363 		ins->fadeout = (uint16_t)pos;
1364 		drawFadeout();
1365 		setSongModifiedFlag();
1366 	}
1367 }
1368 
setVibSpeedScroll(uint32_t pos)1369 void setVibSpeedScroll(uint32_t pos)
1370 {
1371 	instr_t *ins = instr[editor.curInstr];
1372 	if (ins == NULL || editor.curInstr == 0)
1373 	{
1374 		setScrollBarPos(SB_INST_VIBSPEED, 0, false);
1375 		return;
1376 	}
1377 
1378 	if (ins->vibRate != (uint8_t)pos)
1379 	{
1380 		ins->vibRate = (uint8_t)pos;
1381 		drawVibSpeed();
1382 		setSongModifiedFlag();
1383 	}
1384 }
1385 
setVibDepthScroll(uint32_t pos)1386 void setVibDepthScroll(uint32_t pos)
1387 {
1388 	instr_t *ins = instr[editor.curInstr];
1389 	if (ins == NULL || editor.curInstr == 0)
1390 	{
1391 		setScrollBarPos(SB_INST_VIBDEPTH, 0, false);
1392 		return;
1393 	}
1394 
1395 	if (ins->vibDepth != (uint8_t)pos)
1396 	{
1397 		ins->vibDepth = (uint8_t)pos;
1398 		drawVibDepth();
1399 		setSongModifiedFlag();
1400 	}
1401 }
1402 
setVibSweepScroll(uint32_t pos)1403 void setVibSweepScroll(uint32_t pos)
1404 {
1405 	instr_t *ins = instr[editor.curInstr];
1406 	if (ins == NULL || editor.curInstr == 0)
1407 	{
1408 		setScrollBarPos(SB_INST_VIBSWEEP, 0, false);
1409 		return;
1410 	}
1411 
1412 	if (ins->vibSweep != (uint8_t)pos)
1413 	{
1414 		ins->vibSweep = (uint8_t)pos;
1415 		drawVibSweep();
1416 		setSongModifiedFlag();
1417 	}
1418 }
1419 
rbVibWaveSine(void)1420 void rbVibWaveSine(void)
1421 {
1422 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1423 		return;
1424 
1425 	instr[editor.curInstr]->vibType = 0;
1426 
1427 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
1428 	radioButtons[RB_INST_WAVE_SINE].state = RADIOBUTTON_CHECKED;
1429 	showRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
1430 	setSongModifiedFlag();
1431 }
1432 
rbVibWaveSquare(void)1433 void rbVibWaveSquare(void)
1434 {
1435 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1436 		return;
1437 
1438 	instr[editor.curInstr]->vibType = 1;
1439 
1440 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
1441 	radioButtons[RB_INST_WAVE_SQUARE].state = RADIOBUTTON_CHECKED;
1442 	showRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
1443 	setSongModifiedFlag();
1444 }
1445 
rbVibWaveRampDown(void)1446 void rbVibWaveRampDown(void)
1447 {
1448 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1449 		return;
1450 
1451 	instr[editor.curInstr]->vibType = 2;
1452 
1453 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
1454 	radioButtons[RB_INST_WAVE_RAMP_DOWN].state = RADIOBUTTON_CHECKED;
1455 	showRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
1456 	setSongModifiedFlag();
1457 }
1458 
rbVibWaveRampUp(void)1459 void rbVibWaveRampUp(void)
1460 {
1461 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1462 		return;
1463 
1464 	instr[editor.curInstr]->vibType = 3;
1465 
1466 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
1467 	radioButtons[RB_INST_WAVE_RAMP_UP].state = RADIOBUTTON_CHECKED;
1468 	showRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
1469 	setSongModifiedFlag();
1470 }
1471 
cbVEnv(void)1472 void cbVEnv(void)
1473 {
1474 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1475 	{
1476 		checkBoxes[CB_INST_VENV].checked = false;
1477 		drawCheckBox(CB_INST_VENV);
1478 		return;
1479 	}
1480 
1481 	instr[editor.curInstr]->volEnvFlags ^= 1;
1482 	updateVolEnv = true;
1483 
1484 	setSongModifiedFlag();
1485 }
1486 
cbVEnvSus(void)1487 void cbVEnvSus(void)
1488 {
1489 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1490 	{
1491 		checkBoxes[CB_INST_VENV_SUS].checked = false;
1492 		drawCheckBox(CB_INST_VENV_SUS);
1493 		return;
1494 	}
1495 
1496 	instr[editor.curInstr]->volEnvFlags ^= 2;
1497 	updateVolEnv = true;
1498 
1499 	setSongModifiedFlag();
1500 }
1501 
cbVEnvLoop(void)1502 void cbVEnvLoop(void)
1503 {
1504 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1505 	{
1506 		checkBoxes[CB_INST_VENV_LOOP].checked = false;
1507 		drawCheckBox(CB_INST_VENV_LOOP);
1508 		return;
1509 	}
1510 
1511 	instr[editor.curInstr]->volEnvFlags ^= 4;
1512 	updateVolEnv = true;
1513 
1514 	setSongModifiedFlag();
1515 }
1516 
cbPEnv(void)1517 void cbPEnv(void)
1518 {
1519 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1520 	{
1521 		checkBoxes[CB_INST_PENV].checked = false;
1522 		drawCheckBox(CB_INST_PENV);
1523 		return;
1524 	}
1525 
1526 	instr[editor.curInstr]->panEnvFlags ^= 1;
1527 	updatePanEnv = true;
1528 
1529 	setSongModifiedFlag();
1530 }
1531 
cbPEnvSus(void)1532 void cbPEnvSus(void)
1533 {
1534 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1535 	{
1536 		checkBoxes[CB_INST_PENV_SUS].checked = false;
1537 		drawCheckBox(CB_INST_PENV_SUS);
1538 		return;
1539 	}
1540 
1541 	instr[editor.curInstr]->panEnvFlags ^= 2;
1542 	updatePanEnv = true;
1543 
1544 	setSongModifiedFlag();
1545 }
1546 
cbPEnvLoop(void)1547 void cbPEnvLoop(void)
1548 {
1549 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
1550 	{
1551 		checkBoxes[CB_INST_PENV_LOOP].checked = false;
1552 		drawCheckBox(CB_INST_PENV_LOOP);
1553 		return;
1554 	}
1555 
1556 	instr[editor.curInstr]->panEnvFlags ^= 4;
1557 	updatePanEnv = true;
1558 
1559 	setSongModifiedFlag();
1560 }
1561 
pinoaNumberOut(uint16_t xPos,uint16_t yPos,uint8_t fgPalette,uint8_t bgPalette,uint8_t val)1562 static void pinoaNumberOut(uint16_t xPos, uint16_t yPos, uint8_t fgPalette, uint8_t bgPalette, uint8_t val)
1563 {
1564 	assert(val <= 0xF);
1565 
1566 	const uint32_t fg = video.palette[fgPalette];
1567 	const uint32_t bg = video.palette[bgPalette];
1568 	uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
1569 	const uint8_t *srcPtr = &bmp.font8[val * 5];
1570 
1571 	for (int32_t y = 0; y < 7; y++)
1572 	{
1573 		for (int32_t x = 0; x < 5; x++)
1574 			dstPtr[x] = srcPtr[x] ? fg : bg;
1575 
1576 		dstPtr += SCREEN_W;
1577 		srcPtr += 80;
1578 	}
1579 }
1580 
writePianoNumber(uint8_t note,uint8_t key,uint8_t octave)1581 static void writePianoNumber(uint8_t note, uint8_t key, uint8_t octave)
1582 {
1583 	uint8_t number = 0;
1584 	if (instr[editor.curInstr] != NULL && editor.curInstr != 0)
1585 		number = instr[editor.curInstr]->note2SampleLUT[note];
1586 
1587 	const uint16_t x = keyDigitXPos[key] + (octave * 77);
1588 
1589 	if (keyIsBlackTab[key])
1590 		pinoaNumberOut(x, 361, PAL_FORGRND, PAL_BCKGRND, number);
1591 	else
1592 		pinoaNumberOut(x, 385, PAL_BCKGRND, PAL_FORGRND, number);
1593 }
1594 
drawBlackPianoKey(uint8_t key,uint8_t octave,bool keyDown)1595 static void drawBlackPianoKey(uint8_t key, uint8_t octave, bool keyDown)
1596 {
1597 	const uint16_t x = keyXPos[key] + (octave * 77);
1598 	blit(x, 351, &bmp.blackPianoKeys[keyDown * (7*27)], 7, 27);
1599 }
1600 
drawWhitePianoKey(uint8_t key,uint8_t octave,bool keyDown)1601 static void drawWhitePianoKey(uint8_t key, uint8_t octave,  bool keyDown)
1602 {
1603 	const uint16_t x = keyXPos[key] + (octave * 77);
1604 	blit(x, 351, &bmp.whitePianoKeys[(keyDown * (11*46*3)) + whiteKeysBmpOrder[key]], 11, 46);
1605 }
1606 
redrawPiano(void)1607 void redrawPiano(void)
1608 {
1609 	memset(pianoKeyStatus, 0, sizeof (pianoKeyStatus));
1610 	for (uint8_t i = 0; i < 96; i++)
1611 	{
1612 		const uint8_t key = noteTab1[i];
1613 		const uint8_t octave = noteTab2[i];
1614 
1615 		if (keyIsBlackTab[key])
1616 			drawBlackPianoKey(key, octave, false);
1617 		else
1618 			drawWhitePianoKey(key, octave, false);
1619 
1620 		writePianoNumber(i, key, octave);
1621 	}
1622 }
1623 
testPianoKeysMouseDown(bool mouseButtonDown)1624 bool testPianoKeysMouseDown(bool mouseButtonDown)
1625 {
1626 	uint8_t key, octave;
1627 
1628 	if (!ui.instEditorShown)
1629 		return false; // area not clicked
1630 
1631 	if (editor.curInstr == 0 || instr[editor.curInstr] == NULL)
1632 		return true; // area clicked, but don't do anything
1633 
1634 	int32_t mx = mouse.x;
1635 	int32_t my = mouse.y;
1636 
1637 	if (!mouseButtonDown)
1638 	{
1639 		if (my < 351 || my > 396 || mx < 8 || mx > 623)
1640 			return false;
1641 
1642 		mouse.lastUsedObjectType = OBJECT_PIANO;
1643 	}
1644 	else
1645 	{
1646 		my = CLAMP(my, 351, 396);
1647 		mx = CLAMP(mx, 8, 623);
1648 	}
1649 
1650 	mx -= 8;
1651 
1652 	const int32_t quotient  = mx / 77;
1653 	const int32_t remainder = mx % 77;
1654 
1655 	if (my < 378)
1656 	{
1657 		// white keys and black keys (top)
1658 
1659 		octave = (uint8_t)quotient;
1660 		key = mx2PianoKey[remainder];
1661 	}
1662 	else
1663 	{
1664 		// white keys only (bottom)
1665 		const int32_t whiteKeyWidth = 11;
1666 
1667 		octave = (uint8_t)(quotient);
1668 		key = whiteKeyIndex[remainder / whiteKeyWidth];
1669 	}
1670 
1671 	const uint8_t note = (octave * 12) + key;
1672 	if (instr[editor.curInstr]->note2SampleLUT[note] != editor.curSmp)
1673 	{
1674 		instr[editor.curInstr]->note2SampleLUT[note] = editor.curSmp;
1675 		writePianoNumber(note, key, octave);
1676 		setSongModifiedFlag();
1677 	}
1678 
1679 	return true;
1680 }
1681 
drawPiano(chSyncData_t * chSyncData)1682 void drawPiano(chSyncData_t *chSyncData)
1683 {
1684 	bool newStatus[96];
1685 	memset(newStatus, 0, sizeof (newStatus));
1686 
1687 	// find active notes
1688 	if (editor.curInstr > 0)
1689 	{
1690 		if (chSyncData != NULL) // song is playing, use replayer channel state
1691 		{
1692 			syncedChannel_t *c = chSyncData->channels;
1693 			for (int32_t i = 0; i < song.numChannels; i++, c++)
1694 			{
1695 				if (c->instrNum == editor.curInstr && c->pianoNoteNum <= 95)
1696 					newStatus[c->pianoNoteNum] = true;
1697 			}
1698 		}
1699 		else // song is not playing (jamming from keyboard/MIDI)
1700 		{
1701 			channel_t *c = channel;
1702 			for (int32_t i = 0; i < song.numChannels; i++, c++)
1703 			{
1704 				if (c->instrNum == editor.curInstr && c->envSustainActive)
1705 				{
1706 					const int32_t note = getPianoKey(c->finalPeriod, c->finetune, c->relativeNote);
1707 					if (note >= 0 && note <= 95)
1708 						newStatus[note] = true;
1709 				}
1710 			}
1711 		}
1712 	}
1713 
1714 	// draw keys
1715 	for (int32_t i = 0; i < 96; i++)
1716 	{
1717 		const bool keyDown = newStatus[i];
1718 		if (pianoKeyStatus[i] ^ keyDown)
1719 		{
1720 			const uint8_t key = noteTab1[i];
1721 			const uint8_t octave = noteTab2[i];
1722 
1723 			if (keyIsBlackTab[key])
1724 				drawBlackPianoKey(key, octave, keyDown);
1725 			else
1726 				drawWhitePianoKey(key, octave, keyDown);
1727 
1728 			pianoKeyStatus[i] = keyDown;
1729 		}
1730 	}
1731 }
1732 
envelopeLine(int32_t envNum,int16_t x1,int16_t y1,int16_t x2,int16_t y2,uint8_t pal)1733 static void envelopeLine(int32_t envNum, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t pal)
1734 {
1735 	y1 = CLAMP(y1, 0, 66);
1736 	y2 = CLAMP(y2, 0, 66);
1737 	x1 = CLAMP(x1, 0, 335);
1738 	x2 = CLAMP(x2, 0, 335);
1739 
1740 	if (envNum == 0) // volume envelope
1741 	{
1742 		y1 += 189;
1743 		y2 += 189;
1744 	}
1745 	else // panning envelope
1746 	{
1747 		y1 += 276;
1748 		y2 += 276;
1749 	}
1750 
1751 	const int16_t dx = x2 - x1;
1752 	const uint16_t ax = ABS(dx) << 1;
1753 	const int16_t sx = SGN(dx);
1754 	const int16_t dy = y2 - y1;
1755 	const uint16_t ay = ABS(dy) << 1;
1756 	const int16_t sy = SGN(dy);
1757 	int16_t x = x1;
1758 	int16_t y = y1;
1759 
1760 	const uint32_t pal1 = video.palette[PAL_BLCKMRK];
1761 	const uint32_t pal2 = video.palette[PAL_BLCKTXT];
1762 	const uint32_t pixVal = video.palette[pal];
1763 	const int32_t pitch = sy * SCREEN_W;
1764 
1765 	uint32_t *dst32 = &video.frameBuffer[(y * SCREEN_W) + x];
1766 
1767 	// draw line
1768 	if (ax > ay)
1769 	{
1770 		int16_t d = ay - (ax >> 1);
1771 		while (true)
1772 		{
1773 			// invert certain colors
1774 			if (*dst32 != pal2)
1775 			{
1776 				if (*dst32 == pal1)
1777 					*dst32 = pal2;
1778 				else
1779 					*dst32 = pixVal;
1780 			}
1781 
1782 			if (x == x2)
1783 				break;
1784 
1785 			if (d >= 0)
1786 			{
1787 				d -= ax;
1788 				dst32 += pitch;
1789 			}
1790 
1791 			x += sx;
1792 			d += ay;
1793 			dst32 += sx;
1794 		}
1795 	}
1796 	else
1797 	{
1798 		int16_t d = ax - (ay >> 1);
1799 		while (true)
1800 		{
1801 			// invert certain colors
1802 			if (*dst32 != pal2)
1803 			{
1804 				if (*dst32 == pal1)
1805 					*dst32 = pal2;
1806 				else
1807 					*dst32 = pixVal;
1808 			}
1809 
1810 			if (y == y2)
1811 				break;
1812 
1813 			if (d >= 0)
1814 			{
1815 				d -= ay;
1816 				dst32 += sx;
1817 			}
1818 
1819 			y += sy;
1820 			d += ax;
1821 			dst32 += pitch;
1822 		}
1823 	}
1824 }
1825 
envelopePixel(int32_t envNum,int16_t x,int16_t y,uint8_t pal)1826 static void envelopePixel(int32_t envNum, int16_t x, int16_t y, uint8_t pal)
1827 {
1828 	y += (envNum == 0) ? 189 : 276;
1829 	video.frameBuffer[(y * SCREEN_W) + x] = video.palette[pal];
1830 }
1831 
envelopeDot(int32_t envNum,int16_t x,int16_t y)1832 static void envelopeDot(int32_t envNum, int16_t x, int16_t y)
1833 {
1834 	y += (envNum == 0) ? 189 : 276;
1835 
1836 	const uint32_t pixVal = video.palette[PAL_BLCKTXT];
1837 	uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x];
1838 
1839 	for (y = 0; y < 3; y++)
1840 	{
1841 		*dstPtr++ = pixVal;
1842 		*dstPtr++ = pixVal;
1843 		*dstPtr++ = pixVal;
1844 
1845 		dstPtr += SCREEN_W-3;
1846 	}
1847 }
1848 
envelopeVertLine(int32_t envNum,int16_t x,int16_t y,uint8_t pal)1849 static void envelopeVertLine(int32_t envNum, int16_t x, int16_t y, uint8_t pal)
1850 {
1851 	y += (envNum == 0) ? 189 : 276;
1852 
1853 	const uint32_t pixVal1 = video.palette[pal];
1854 	const uint32_t pixVal2 = video.palette[PAL_BLCKTXT];
1855 
1856 	uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x];
1857 	for (y = 0; y < 33; y++)
1858 	{
1859 		if (*dstPtr != pixVal2)
1860 			*dstPtr = pixVal1;
1861 
1862 		dstPtr += SCREEN_W*2;
1863 	}
1864 }
1865 
writeEnvelope(int32_t envNum)1866 static void writeEnvelope(int32_t envNum)
1867 {
1868 	uint8_t selected;
1869 	int16_t i, nd, sp, ls, le, (*curEnvP)[2];
1870 
1871 	instr_t *ins = instr[editor.curInstr];
1872 
1873 	// clear envelope area
1874 	if (envNum == 0)
1875 		clearRect(5, 189, 333, 67);
1876 	else
1877 		clearRect(5, 276, 333, 67);
1878 
1879 	// draw dotted x/y lines
1880 	for (i = 0; i <= 32; i++) envelopePixel(envNum, 5, 1 + i * 2, PAL_PATTEXT);
1881 	for (i = 0; i <= 8; i++) envelopePixel(envNum, 4, 1 + i * 8, PAL_PATTEXT);
1882 	for (i = 0; i <= 162; i++) envelopePixel(envNum, 8 + i *  2, 65, PAL_PATTEXT);
1883 	for (i = 0; i <= 6; i++) envelopePixel(envNum, 8 + i * 50, 66, PAL_PATTEXT);
1884 
1885 	// draw center line on pan envelope
1886 	if (envNum == 1)
1887 		envelopeLine(envNum, 8, 33, 332, 33, PAL_BLCKMRK);
1888 
1889 	if (ins == NULL)
1890 		return;
1891 
1892 	// collect variables
1893 
1894 	if (envNum == 0) // volume envelope
1895 	{
1896 		nd = ins->volEnvLength;
1897 		if (ins->volEnvFlags & ENV_SUSTAIN)
1898 			sp = ins->volEnvSustain;
1899 		else
1900 			sp = -1;
1901 
1902 		if (ins->volEnvFlags & ENV_LOOP)
1903 		{
1904 			ls = ins->volEnvLoopStart;
1905 			le = ins->volEnvLoopEnd;
1906 		}
1907 		else
1908 		{
1909 			ls = -1;
1910 			le = -1;
1911 		}
1912 
1913 		curEnvP = ins->volEnvPoints;
1914 		selected = editor.currVolEnvPoint;
1915 	}
1916 	else // panning envelope
1917 	{
1918 		nd = ins->panEnvLength;
1919 		if (ins->panEnvFlags & ENV_SUSTAIN)
1920 			sp = ins->panEnvSustain;
1921 		else
1922 			sp = -1;
1923 
1924 		if (ins->panEnvFlags & ENV_LOOP)
1925 		{
1926 			ls = ins->panEnvLoopStart;
1927 			le = ins->panEnvLoopEnd;
1928 		}
1929 		else
1930 		{
1931 			ls = -1;
1932 			le = -1;
1933 		}
1934 
1935 		curEnvP = ins->panEnvPoints;
1936 		selected = editor.currPanEnvPoint;
1937 	}
1938 
1939 	if (nd > 12)
1940 		nd = 12;
1941 
1942 	int16_t lx = 0;
1943 	int16_t ly = 0;
1944 
1945 	// draw envelope
1946 	for (i = 0; i < nd; i++)
1947 	{
1948 		int16_t x = curEnvP[i][0];
1949 		int16_t y = curEnvP[i][1];
1950 
1951 		x = CLAMP(x, 0, 324);
1952 
1953 		if (envNum == 0) // volume envelope
1954 			y = CLAMP(y, 0, 64);
1955 		else // panning envelope
1956 			y = CLAMP(y, 0, 63);
1957 
1958 		if ((uint16_t)curEnvP[i][0] <= 324)
1959 		{
1960 			envelopeDot(envNum, 7 + x, 64 - y);
1961 
1962 			// draw "envelope selected" data
1963 			if (i == selected)
1964 			{
1965 				envelopeLine(envNum, 5  + x, 64 - y, 5  + x, 66 - y, PAL_BLCKTXT);
1966 				envelopeLine(envNum, 11 + x, 64 - y, 11 + x, 66 - y, PAL_BLCKTXT);
1967 				envelopePixel(envNum, 5, 65 - y, PAL_BLCKTXT);
1968 				envelopePixel(envNum, 8 + x, 65, PAL_BLCKTXT);
1969 			}
1970 
1971 			// draw loop start marker
1972 			if (i == ls)
1973 			{
1974 				envelopeLine(envNum, x + 6, 1, x + 10, 1, PAL_PATTEXT);
1975 				envelopeLine(envNum, x + 7, 2, x +  9, 2, PAL_PATTEXT);
1976 				envelopeVertLine(envNum, x + 8, 1, PAL_PATTEXT);
1977 			}
1978 
1979 			// draw sustain marker
1980 			if (i == sp)
1981 				envelopeVertLine(envNum, x + 8, 1, PAL_BLCKTXT);
1982 
1983 			// draw loop end marker
1984 			if (i == le)
1985 			{
1986 				envelopeLine(envNum, x + 6, 65, x + 10, 65, PAL_PATTEXT);
1987 				envelopeLine(envNum, x + 7, 64, x +  9, 64, PAL_PATTEXT);
1988 				envelopeVertLine(envNum, x + 8, 1, PAL_PATTEXT);
1989 			}
1990 		}
1991 
1992 		// draw envelope line
1993 		if (i > 0 && lx < x)
1994 			envelopeLine(envNum, lx + 8, 65 - ly, x + 8, 65 - y, PAL_PATTEXT);
1995 
1996 		lx = x;
1997 		ly = y;
1998 	}
1999 }
2000 
drawVolEnvCoords(int16_t tick,int16_t val)2001 static void drawVolEnvCoords(int16_t tick, int16_t val)
2002 {
2003 	char str[4];
2004 
2005 	tick = CLAMP(tick, 0, 324);
2006 	sprintf(str, "%03d", tick);
2007 	textOutTinyOutline(326, 190, str);
2008 
2009 	val = CLAMP(val, 0, 64);
2010 	sprintf(str, "%02d", val);
2011 	textOutTinyOutline(330, 198, str);
2012 }
2013 
drawPanEnvCoords(int16_t tick,int16_t val)2014 static void drawPanEnvCoords(int16_t tick, int16_t val)
2015 {
2016 	bool negative = false;
2017 	char str[4];
2018 
2019 	tick = CLAMP(tick, 0, 324);
2020 	sprintf(str, "%03d", tick);
2021 	textOutTinyOutline(326, 277, str);
2022 
2023 	val -= 32;
2024 	val = CLAMP(val, -32, 31);
2025 	if (val < 0)
2026 	{
2027 		negative = true;
2028 		val = -val;
2029 	}
2030 
2031 	if (negative) // draw minus sign
2032 	{
2033 		// outline
2034 		hLine(326, 287, 3, PAL_BCKGRND);
2035 		hLine(326, 289, 3, PAL_BCKGRND);
2036 		video.frameBuffer[(288 * SCREEN_W) + 325] = video.palette[PAL_BCKGRND];
2037 		video.frameBuffer[(288 * SCREEN_W) + 329] = video.palette[PAL_BCKGRND];
2038 
2039 		hLine(326, 288, 3, PAL_FORGRND);
2040 	}
2041 
2042 	sprintf(str, "%02d", val);
2043 	textOutTinyOutline(330, 285, str);
2044 }
2045 
handleInstEditorRedrawing(void)2046 void handleInstEditorRedrawing(void)
2047 {
2048 	int16_t tick, val;
2049 
2050 	instr_t *ins = instr[editor.curInstr];
2051 
2052 	if (updateVolEnv)
2053 	{
2054 		updateVolEnv = false;
2055 		writeEnvelope(0);
2056 
2057 		tick = 0;
2058 		val = 0;
2059 
2060 		if (ins != NULL && ins->volEnvLength > 0)
2061 		{
2062 			tick = ins->volEnvPoints[editor.currVolEnvPoint][0];
2063 			val = ins->volEnvPoints[editor.currVolEnvPoint][1];
2064 		}
2065 
2066 		drawVolEnvCoords(tick, val);
2067 	}
2068 
2069 	if (updatePanEnv)
2070 	{
2071 		updatePanEnv = false;
2072 		writeEnvelope(1);
2073 
2074 		tick = 0;
2075 		val = 32;
2076 
2077 		if (ins != NULL && ins->panEnvLength > 0)
2078 		{
2079 			tick = ins->panEnvPoints[editor.currPanEnvPoint][0];
2080 			val = ins->panEnvPoints[editor.currPanEnvPoint][1];
2081 		}
2082 
2083 		drawPanEnvCoords(tick, val);
2084 	}
2085 }
2086 
hideInstEditor(void)2087 void hideInstEditor(void)
2088 {
2089 	ui.instEditorShown = false;
2090 
2091 	hideScrollBar(SB_INST_VOL);
2092 	hideScrollBar(SB_INST_PAN);
2093 	hideScrollBar(SB_INST_FTUNE);
2094 	hideScrollBar(SB_INST_FADEOUT);
2095 	hideScrollBar(SB_INST_VIBSPEED);
2096 	hideScrollBar(SB_INST_VIBDEPTH);
2097 	hideScrollBar(SB_INST_VIBSWEEP);
2098 
2099 	hidePushButton(PB_INST_VDEF1);
2100 	hidePushButton(PB_INST_VDEF2);
2101 	hidePushButton(PB_INST_VDEF3);
2102 	hidePushButton(PB_INST_VDEF4);
2103 	hidePushButton(PB_INST_VDEF5);
2104 	hidePushButton(PB_INST_VDEF6);
2105 	hidePushButton(PB_INST_PDEF1);
2106 	hidePushButton(PB_INST_PDEF2);
2107 	hidePushButton(PB_INST_PDEF3);
2108 	hidePushButton(PB_INST_PDEF4);
2109 	hidePushButton(PB_INST_PDEF5);
2110 	hidePushButton(PB_INST_PDEF6);
2111 	hidePushButton(PB_INST_VP_ADD);
2112 	hidePushButton(PB_INST_VP_DEL);
2113 	hidePushButton(PB_INST_VS_UP);
2114 	hidePushButton(PB_INST_VS_DOWN);
2115 	hidePushButton(PB_INST_VREPS_UP);
2116 	hidePushButton(PB_INST_VREPS_DOWN);
2117 	hidePushButton(PB_INST_VREPE_UP);
2118 	hidePushButton(PB_INST_VREPE_DOWN);
2119 	hidePushButton(PB_INST_PP_ADD);
2120 	hidePushButton(PB_INST_PP_DEL);
2121 	hidePushButton(PB_INST_PS_UP);
2122 	hidePushButton(PB_INST_PS_DOWN);
2123 	hidePushButton(PB_INST_PREPS_UP);
2124 	hidePushButton(PB_INST_PREPS_DOWN);
2125 	hidePushButton(PB_INST_PREPE_UP);
2126 	hidePushButton(PB_INST_PREPE_DOWN);
2127 	hidePushButton(PB_INST_VOL_DOWN);
2128 	hidePushButton(PB_INST_VOL_UP);
2129 	hidePushButton(PB_INST_PAN_DOWN);
2130 	hidePushButton(PB_INST_PAN_UP);
2131 	hidePushButton(PB_INST_FTUNE_DOWN);
2132 	hidePushButton(PB_INST_FTUNE_UP);
2133 	hidePushButton(PB_INST_FADEOUT_DOWN);
2134 	hidePushButton(PB_INST_FADEOUT_UP);
2135 	hidePushButton(PB_INST_VIBSPEED_DOWN);
2136 	hidePushButton(PB_INST_VIBSPEED_UP);
2137 	hidePushButton(PB_INST_VIBDEPTH_DOWN);
2138 	hidePushButton(PB_INST_VIBDEPTH_UP);
2139 	hidePushButton(PB_INST_VIBSWEEP_DOWN);
2140 	hidePushButton(PB_INST_VIBSWEEP_UP);
2141 	hidePushButton(PB_INST_EXIT);
2142 	hidePushButton(PB_INST_OCT_UP);
2143 	hidePushButton(PB_INST_HALFTONE_UP);
2144 	hidePushButton(PB_INST_OCT_DOWN);
2145 	hidePushButton(PB_INST_HALFTONE_DOWN);
2146 
2147 	hideCheckBox(CB_INST_VENV);
2148 	hideCheckBox(CB_INST_VENV_SUS);
2149 	hideCheckBox(CB_INST_VENV_LOOP);
2150 	hideCheckBox(CB_INST_PENV);
2151 	hideCheckBox(CB_INST_PENV_SUS);
2152 	hideCheckBox(CB_INST_PENV_LOOP);
2153 
2154 	hideRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
2155 }
2156 
exitInstEditor(void)2157 void exitInstEditor(void)
2158 {
2159 	hideInstEditor();
2160 	showPatternEditor();
2161 }
2162 
updateInstEditor(void)2163 void updateInstEditor(void)
2164 {
2165 	uint16_t tmpID;
2166 	sample_t *s;
2167 	instr_t *ins = getCurDispInstr();
2168 
2169 	if (instr[editor.curInstr] == NULL)
2170 		s = &ins->smp[0];
2171 	else
2172 		s = &ins->smp[editor.curSmp];
2173 
2174 	// update instrument editor extension
2175 	if (ui.instEditorExtShown)
2176 	{
2177 		checkBoxes[CB_INST_EXT_MIDI].checked = ins->midiOn ? true : false;
2178 		checkBoxes[CB_INST_EXT_MUTE].checked = ins->mute ? true : false;
2179 
2180 		setScrollBarPos(SB_INST_EXT_MIDI_CH, ins->midiChannel, false);
2181 		setScrollBarPos(SB_INST_EXT_MIDI_PRG, ins->midiProgram, false);
2182 		setScrollBarPos(SB_INST_EXT_MIDI_BEND, ins->midiBend, false);
2183 
2184 		drawCheckBox(CB_INST_EXT_MIDI);
2185 		drawCheckBox(CB_INST_EXT_MUTE);
2186 
2187 		drawMIDICh();
2188 		drawMIDIPrg();
2189 		drawMIDIBend();
2190 	}
2191 
2192 	if (!ui.instEditorShown)
2193 		return;
2194 
2195 	drawVolEnvSus();
2196 	drawVolEnvRepS();
2197 	drawVolEnvRepE();
2198 	drawPanEnvSus();
2199 	drawPanEnvRepS();
2200 	drawPanEnvRepE();
2201 	drawVolume();
2202 	drawPanning();
2203 	drawFineTune();
2204 	drawFadeout();
2205 	drawVibSpeed();
2206 	drawVibDepth();
2207 	drawVibSweep();
2208 	drawC4Rate();
2209 	drawRelativeNote();
2210 
2211 	// set scroll bars
2212 	setScrollBarPos(SB_INST_VOL, s->volume, false);
2213 	setScrollBarPos(SB_INST_PAN, s->panning, false);
2214 	setScrollBarPos(SB_INST_FTUNE, 128 + s->finetune, false);
2215 	setScrollBarPos(SB_INST_FADEOUT, ins->fadeout, false);
2216 	setScrollBarPos(SB_INST_VIBSPEED, ins->vibRate, false);
2217 	setScrollBarPos(SB_INST_VIBDEPTH, ins->vibDepth, false);
2218 	setScrollBarPos(SB_INST_VIBSWEEP, ins->vibSweep, false);
2219 
2220 	// set radio buttons
2221 
2222 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
2223 	switch (ins->vibType)
2224 	{
2225 		default:
2226 		case 0: tmpID = RB_INST_WAVE_SINE;      break;
2227 		case 1: tmpID = RB_INST_WAVE_SQUARE;    break;
2228 		case 2: tmpID = RB_INST_WAVE_RAMP_DOWN; break;
2229 		case 3: tmpID = RB_INST_WAVE_RAMP_UP;   break;
2230 	}
2231 
2232 	radioButtons[tmpID].state = RADIOBUTTON_CHECKED;
2233 
2234 	// set checkboxes
2235 
2236 	checkBoxes[CB_INST_VENV].checked      = (ins->volEnvFlags & ENV_ENABLED) ? true : false;
2237 	checkBoxes[CB_INST_VENV_SUS].checked  = (ins->volEnvFlags & ENV_SUSTAIN) ? true : false;
2238 	checkBoxes[CB_INST_VENV_LOOP].checked = (ins->volEnvFlags & ENV_LOOP)    ? true : false;
2239 
2240 	checkBoxes[CB_INST_PENV].checked      = (ins->panEnvFlags & ENV_ENABLED) ? true : false;
2241 	checkBoxes[CB_INST_PENV_SUS].checked  = (ins->panEnvFlags & ENV_SUSTAIN) ? true : false;
2242 	checkBoxes[CB_INST_PENV_LOOP].checked = (ins->panEnvFlags & ENV_LOOP)    ? true : false;
2243 
2244 	if (editor.currVolEnvPoint >= ins->volEnvLength) editor.currVolEnvPoint = 0;
2245 	if (editor.currPanEnvPoint >= ins->panEnvLength) editor.currPanEnvPoint = 0;
2246 
2247 	showRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
2248 
2249 	drawCheckBox(CB_INST_VENV);
2250 	drawCheckBox(CB_INST_VENV_SUS);
2251 	drawCheckBox(CB_INST_VENV_LOOP);
2252 	drawCheckBox(CB_INST_PENV);
2253 	drawCheckBox(CB_INST_PENV_SUS);
2254 	drawCheckBox(CB_INST_PENV_LOOP);
2255 
2256 	updateVolEnv = true;
2257 	updatePanEnv = true;
2258 
2259 	redrawPiano();
2260 }
2261 
showInstEditor(void)2262 void showInstEditor(void)
2263 {
2264 	if (ui.extended) exitPatternEditorExtended();
2265 	if (ui.sampleEditorShown) hideSampleEditor();
2266 	if (ui.sampleEditorExtShown) hideSampleEditorExt();
2267 
2268 	hidePatternEditor();
2269 	ui.instEditorShown = true;
2270 
2271 	drawFramework(0,   173, 438,  87, FRAMEWORK_TYPE1);
2272 	drawFramework(0,   260, 438,  87, FRAMEWORK_TYPE1);
2273 	drawFramework(0,   347, 632,  53, FRAMEWORK_TYPE1);
2274 	drawFramework(438, 173, 194,  45, FRAMEWORK_TYPE1);
2275 	drawFramework(438, 218, 194,  76, FRAMEWORK_TYPE1);
2276 	drawFramework(438, 294, 194,  53, FRAMEWORK_TYPE1);
2277 	drawFramework(2,   188, 337,  70, FRAMEWORK_TYPE2);
2278 	drawFramework(2,   275, 337,  70, FRAMEWORK_TYPE2);
2279 	drawFramework(2,   349, 628,  49, FRAMEWORK_TYPE2);
2280 	drawFramework(593, 296,  36,  15, FRAMEWORK_TYPE2);
2281 
2282 	textOutShadow(20,  176, PAL_FORGRND, PAL_DSKTOP2, "Volume envelope:");
2283 	textOutShadow(153, 176, PAL_FORGRND, PAL_DSKTOP2, "Predef.");
2284 	textOutShadow(358, 194, PAL_FORGRND, PAL_DSKTOP2, "Sustain:");
2285 	textOutShadow(342, 206, PAL_FORGRND, PAL_DSKTOP2, "Point");
2286 	textOutShadow(358, 219, PAL_FORGRND, PAL_DSKTOP2, "Env.loop:");
2287 	textOutShadow(342, 233, PAL_FORGRND, PAL_DSKTOP2, "Start");
2288 	textOutShadow(342, 247, PAL_FORGRND, PAL_DSKTOP2, "End");
2289 	textOutShadow(20,  263, PAL_FORGRND, PAL_DSKTOP2, "Panning envelope:");
2290 	textOutShadow(152, 263, PAL_FORGRND, PAL_DSKTOP2, "Predef.");
2291 	textOutShadow(358, 281, PAL_FORGRND, PAL_DSKTOP2, "Sustain:");
2292 	textOutShadow(342, 293, PAL_FORGRND, PAL_DSKTOP2, "Point");
2293 	textOutShadow(358, 306, PAL_FORGRND, PAL_DSKTOP2, "Env.loop:");
2294 	textOutShadow(342, 320, PAL_FORGRND, PAL_DSKTOP2, "Start");
2295 	textOutShadow(342, 334, PAL_FORGRND, PAL_DSKTOP2, "End");
2296 	textOutShadow(443, 177, PAL_FORGRND, PAL_DSKTOP2, "Volume");
2297 	textOutShadow(443, 191, PAL_FORGRND, PAL_DSKTOP2, "Panning");
2298 	textOutShadow(443, 205, PAL_FORGRND, PAL_DSKTOP2, "F.tune");
2299 	textOutShadow(442, 222, PAL_FORGRND, PAL_DSKTOP2, "Fadeout");
2300 	textOutShadow(442, 236, PAL_FORGRND, PAL_DSKTOP2, "Vib.speed");
2301 	textOutShadow(442, 250, PAL_FORGRND, PAL_DSKTOP2, "Vib.depth");
2302 	textOutShadow(442, 264, PAL_FORGRND, PAL_DSKTOP2, "Vib.sweep");
2303 	textOutShadow(442, 299, PAL_FORGRND, PAL_DSKTOP2, "C4=");
2304 	textOutShadow(537, 299, PAL_FORGRND, PAL_DSKTOP2, "Rel. note");
2305 
2306 	showScrollBar(SB_INST_VOL);
2307 	showScrollBar(SB_INST_PAN);
2308 	showScrollBar(SB_INST_FTUNE);
2309 	showScrollBar(SB_INST_FADEOUT);
2310 	showScrollBar(SB_INST_VIBSPEED);
2311 	showScrollBar(SB_INST_VIBDEPTH);
2312 	showScrollBar(SB_INST_VIBSWEEP);
2313 
2314 	showPushButton(PB_INST_VDEF1);
2315 	showPushButton(PB_INST_VDEF2);
2316 	showPushButton(PB_INST_VDEF3);
2317 	showPushButton(PB_INST_VDEF4);
2318 	showPushButton(PB_INST_VDEF5);
2319 	showPushButton(PB_INST_VDEF6);
2320 	showPushButton(PB_INST_PDEF1);
2321 	showPushButton(PB_INST_PDEF2);
2322 	showPushButton(PB_INST_PDEF3);
2323 	showPushButton(PB_INST_PDEF4);
2324 	showPushButton(PB_INST_PDEF5);
2325 	showPushButton(PB_INST_PDEF6);
2326 	showPushButton(PB_INST_VP_ADD);
2327 	showPushButton(PB_INST_VP_DEL);
2328 	showPushButton(PB_INST_VS_UP);
2329 	showPushButton(PB_INST_VS_DOWN);
2330 	showPushButton(PB_INST_VREPS_UP);
2331 	showPushButton(PB_INST_VREPS_DOWN);
2332 	showPushButton(PB_INST_VREPE_UP);
2333 	showPushButton(PB_INST_VREPE_DOWN);
2334 	showPushButton(PB_INST_PP_ADD);
2335 	showPushButton(PB_INST_PP_DEL);
2336 	showPushButton(PB_INST_PS_UP);
2337 	showPushButton(PB_INST_PS_DOWN);
2338 	showPushButton(PB_INST_PREPS_UP);
2339 	showPushButton(PB_INST_PREPS_DOWN);
2340 	showPushButton(PB_INST_PREPE_UP);
2341 	showPushButton(PB_INST_PREPE_DOWN);
2342 	showPushButton(PB_INST_VOL_DOWN);
2343 	showPushButton(PB_INST_VOL_UP);
2344 	showPushButton(PB_INST_PAN_DOWN);
2345 	showPushButton(PB_INST_PAN_UP);
2346 	showPushButton(PB_INST_FTUNE_DOWN);
2347 	showPushButton(PB_INST_FTUNE_UP);
2348 	showPushButton(PB_INST_FADEOUT_DOWN);
2349 	showPushButton(PB_INST_FADEOUT_UP);
2350 	showPushButton(PB_INST_VIBSPEED_DOWN);
2351 	showPushButton(PB_INST_VIBSPEED_UP);
2352 	showPushButton(PB_INST_VIBDEPTH_DOWN);
2353 	showPushButton(PB_INST_VIBDEPTH_UP);
2354 	showPushButton(PB_INST_VIBSWEEP_DOWN);
2355 	showPushButton(PB_INST_VIBSWEEP_UP);
2356 	showPushButton(PB_INST_EXIT);
2357 	showPushButton(PB_INST_OCT_UP);
2358 	showPushButton(PB_INST_HALFTONE_UP);
2359 	showPushButton(PB_INST_OCT_DOWN);
2360 	showPushButton(PB_INST_HALFTONE_DOWN);
2361 
2362 	showCheckBox(CB_INST_VENV);
2363 	showCheckBox(CB_INST_VENV_SUS);
2364 	showCheckBox(CB_INST_VENV_LOOP);
2365 	showCheckBox(CB_INST_PENV);
2366 	showCheckBox(CB_INST_PENV_SUS);
2367 	showCheckBox(CB_INST_PENV_LOOP);
2368 
2369 	// draw auto-vibrato waveforms
2370 	blitFast(455, 279, &bmp.vibratoWaveforms[0*(12*10)], 12, 10);
2371 	blitFast(485, 279, &bmp.vibratoWaveforms[1*(12*10)], 12, 10);
2372 	blitFast(515, 279, &bmp.vibratoWaveforms[2*(12*10)], 12, 10);
2373 	blitFast(545, 279, &bmp.vibratoWaveforms[3*(12*10)], 12, 10);
2374 
2375 	showRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
2376 
2377 	updateInstEditor();
2378 	redrawPiano();
2379 }
2380 
toggleInstEditor(void)2381 void toggleInstEditor(void)
2382 {
2383 	if (ui.sampleEditorShown)
2384 		hideSampleEditor();
2385 
2386 	if (ui.instEditorShown)
2387 	{
2388 		exitInstEditor();
2389 	}
2390 	else
2391 	{
2392 		hidePatternEditor();
2393 		showInstEditor();
2394 	}
2395 }
2396 
testInstrVolEnvMouseDown(bool mouseButtonDown)2397 bool testInstrVolEnvMouseDown(bool mouseButtonDown)
2398 {
2399 	int32_t minX, maxX;
2400 
2401 	if (!ui.instEditorShown || editor.curInstr == 0 || instr[editor.curInstr] == NULL)
2402 		return false;
2403 
2404 	instr_t *ins = instr[editor.curInstr];
2405 
2406 	uint8_t ant = ins->volEnvLength;
2407 	if (ant > 12)
2408 		ant = 12;
2409 
2410 	int32_t mx = mouse.x;
2411 	int32_t my = mouse.y;
2412 
2413 	if (!mouseButtonDown)
2414 	{
2415 		if (my < 189 || my > 256 || mx < 7 || mx > 334)
2416 			return false;
2417 
2418 		if (ins->volEnvLength == 0)
2419 			return true;
2420 
2421 		lastMouseX = mx;
2422 		lastMouseY = my;
2423 
2424 		for (uint8_t i = 0; i < ant; i++)
2425 		{
2426 			const int32_t x = 8 + ins->volEnvPoints[i][0];
2427 			const int32_t y = 190 + (64 - ins->volEnvPoints[i][1]);
2428 
2429 			if (mx >= x-2 && mx <= x+2 && my >= y-2 && my <= y+2)
2430 			{
2431 				editor.currVolEnvPoint = i;
2432 				mouse.lastUsedObjectType = OBJECT_INSVOLENV;
2433 
2434 				saveMouseX = 8 + (lastMouseX - x);
2435 				saveMouseY = 190 + (lastMouseY - y);
2436 
2437 				updateVolEnv = true;
2438 				break;
2439 			}
2440 		}
2441 
2442 		return true;
2443 	}
2444 
2445 	if (ins->volEnvLength == 0)
2446 		return true;
2447 
2448 	if (mx != lastMouseX)
2449 	{
2450 		lastMouseX = mx;
2451 
2452 		if (ant > 1 && editor.currVolEnvPoint > 0)
2453 		{
2454 			mx -= saveMouseX;
2455 			mx = CLAMP(mx, 0, 324);
2456 
2457 			if (editor.currVolEnvPoint == ant-1)
2458 			{
2459 				minX = ins->volEnvPoints[editor.currVolEnvPoint-1][0] + 1;
2460 				maxX = 324;
2461 			}
2462 			else
2463 			{
2464 				minX = ins->volEnvPoints[editor.currVolEnvPoint-1][0] + 1;
2465 				maxX = ins->volEnvPoints[editor.currVolEnvPoint+1][0] - 1;
2466 			}
2467 
2468 			minX = CLAMP(minX, 0, 324);
2469 			maxX = CLAMP(maxX, 0, 324);
2470 
2471 			ins->volEnvPoints[editor.currVolEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX));
2472 			updateVolEnv = true;
2473 
2474 			setSongModifiedFlag();
2475 		}
2476 	}
2477 
2478 	if (my != lastMouseY)
2479 	{
2480 		lastMouseY = my;
2481 
2482 		my -= saveMouseY;
2483 		my = 64 - CLAMP(my, 0, 64);
2484 
2485 		ins->volEnvPoints[editor.currVolEnvPoint][1] = (int16_t)my;
2486 		updateVolEnv = true;
2487 
2488 		setSongModifiedFlag();
2489 	}
2490 
2491 	return true;
2492 }
2493 
testInstrPanEnvMouseDown(bool mouseButtonDown)2494 bool testInstrPanEnvMouseDown(bool mouseButtonDown)
2495 {
2496 	int32_t minX, maxX;
2497 
2498 	if (!ui.instEditorShown || editor.curInstr == 0 || instr[editor.curInstr] == NULL)
2499 		return false;
2500 
2501 	instr_t *ins = instr[editor.curInstr];
2502 
2503 	uint8_t ant = ins->panEnvLength;
2504 	if (ant > 12)
2505 		ant = 12;
2506 
2507 	int32_t mx = mouse.x;
2508 	int32_t my = mouse.y;
2509 
2510 	if (!mouseButtonDown)
2511 	{
2512 		if (my < 277 || my > 343 || mx < 7 || mx > 334)
2513 			return false;
2514 
2515 		if (ins->panEnvLength == 0)
2516 			return true;
2517 
2518 		lastMouseX = mx;
2519 		lastMouseY = my;
2520 
2521 		for (uint8_t i = 0; i < ant; i++)
2522 		{
2523 			const int32_t x = 8 + ins->panEnvPoints[i][0];
2524 			const int32_t y = 277 + (63 - ins->panEnvPoints[i][1]);
2525 
2526 			if (mx >= x-2 && mx <= x+2 && my >= y-2 && my <= y+2)
2527 			{
2528 				editor.currPanEnvPoint = i;
2529 				mouse.lastUsedObjectType = OBJECT_INSPANENV;
2530 
2531 				saveMouseX = lastMouseX - x + 8;
2532 				saveMouseY = lastMouseY - y + 277;
2533 
2534 				updatePanEnv = true;
2535 				break;
2536 			}
2537 		}
2538 
2539 		return true;
2540 	}
2541 
2542 	if (ins->panEnvLength == 0)
2543 		return true;
2544 
2545 	if (mx != lastMouseX)
2546 	{
2547 		lastMouseX = mx;
2548 
2549 		if (ant > 1 && editor.currPanEnvPoint > 0)
2550 		{
2551 			mx -= saveMouseX;
2552 			mx = CLAMP(mx, 0, 324);
2553 
2554 			if (editor.currPanEnvPoint == ant-1)
2555 			{
2556 				minX = ins->panEnvPoints[editor.currPanEnvPoint-1][0] + 1;
2557 				maxX = 324;
2558 			}
2559 			else
2560 			{
2561 				minX = ins->panEnvPoints[editor.currPanEnvPoint-1][0] + 1;
2562 				maxX = ins->panEnvPoints[editor.currPanEnvPoint+1][0] - 1;
2563 			}
2564 
2565 			minX = CLAMP(minX, 0, 324);
2566 			maxX = CLAMP(maxX, 0, 324);
2567 
2568 			ins->panEnvPoints[editor.currPanEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX));
2569 			updatePanEnv = true;
2570 
2571 			setSongModifiedFlag();
2572 		}
2573 	}
2574 
2575 	if (my != lastMouseY)
2576 	{
2577 		lastMouseY = my;
2578 
2579 		my -= saveMouseY;
2580 		my  = 63 - CLAMP(my, 0, 63);
2581 
2582 		ins->panEnvPoints[editor.currPanEnvPoint][1] = (int16_t)my;
2583 		updatePanEnv = true;
2584 
2585 		setSongModifiedFlag();
2586 	}
2587 
2588 	return true;
2589 }
2590 
cbInstMidiEnable(void)2591 void cbInstMidiEnable(void)
2592 {
2593 	if (editor.curInstr == 0 || instr[editor.curInstr] == NULL)
2594 	{
2595 		checkBoxes[CB_INST_EXT_MIDI].checked = false;
2596 		drawCheckBox(CB_INST_EXT_MIDI);
2597 		return;
2598 	}
2599 
2600 	instr[editor.curInstr]->midiOn ^= 1;
2601 	setSongModifiedFlag();
2602 }
2603 
cbInstMuteComputer(void)2604 void cbInstMuteComputer(void)
2605 {
2606 	if (editor.curInstr == 0 || instr[editor.curInstr] == NULL)
2607 	{
2608 		checkBoxes[CB_INST_EXT_MUTE].checked = false;
2609 		drawCheckBox(CB_INST_EXT_MUTE);
2610 		return;
2611 	}
2612 
2613 	instr[editor.curInstr]->mute ^= 1;
2614 	setSongModifiedFlag();
2615 }
2616 
drawInstEditorExt(void)2617 void drawInstEditorExt(void)
2618 {
2619 	instr_t *ins = instr[editor.curInstr];
2620 
2621 	drawFramework(0,  92, 291, 17, FRAMEWORK_TYPE1);
2622 	drawFramework(0, 109, 291, 19, FRAMEWORK_TYPE1);
2623 	drawFramework(0, 128, 291, 45, FRAMEWORK_TYPE1);
2624 
2625 	textOutShadow(4,   96,  PAL_FORGRND, PAL_DSKTOP2, "Instrument Editor Extension:");
2626 	textOutShadow(20,  114, PAL_FORGRND, PAL_DSKTOP2, "Instrument MIDI enable");
2627 	textOutShadow(189, 114, PAL_FORGRND, PAL_DSKTOP2, "Mute computer");
2628 	textOutShadow(4,   132, PAL_FORGRND, PAL_DSKTOP2, "MIDI transmit channel");
2629 	textOutShadow(4,   146, PAL_FORGRND, PAL_DSKTOP2, "MIDI program");
2630 	textOutShadow(4,   160, PAL_FORGRND, PAL_DSKTOP2, "Bender range (halftones)");
2631 
2632 	if (ins == NULL)
2633 	{
2634 		checkBoxes[CB_INST_EXT_MIDI].checked = false;
2635 		checkBoxes[CB_INST_EXT_MUTE].checked = false;
2636 		setScrollBarPos(SB_INST_EXT_MIDI_CH, 0, false);
2637 		setScrollBarPos(SB_INST_EXT_MIDI_PRG, 0, false);
2638 		setScrollBarPos(SB_INST_EXT_MIDI_BEND, 0, false);
2639 	}
2640 	else
2641 	{
2642 		checkBoxes[CB_INST_EXT_MIDI].checked = ins->midiOn ? true : false;
2643 		checkBoxes[CB_INST_EXT_MUTE].checked = ins->mute ? true : false;
2644 		setScrollBarPos(SB_INST_EXT_MIDI_CH, ins->midiChannel, false);
2645 		setScrollBarPos(SB_INST_EXT_MIDI_PRG, ins->midiProgram, false);
2646 		setScrollBarPos(SB_INST_EXT_MIDI_BEND, ins->midiBend, false);
2647 	}
2648 
2649 	showCheckBox(CB_INST_EXT_MIDI);
2650 	showCheckBox(CB_INST_EXT_MUTE);
2651 
2652 	showScrollBar(SB_INST_EXT_MIDI_CH);
2653 	showScrollBar(SB_INST_EXT_MIDI_PRG);
2654 	showScrollBar(SB_INST_EXT_MIDI_BEND);
2655 
2656 	showPushButton(PB_INST_EXT_MIDI_CH_DOWN);
2657 	showPushButton(PB_INST_EXT_MIDI_CH_UP);
2658 	showPushButton(PB_INST_EXT_MIDI_PRG_DOWN);
2659 	showPushButton(PB_INST_EXT_MIDI_PRG_UP);
2660 	showPushButton(PB_INST_EXT_MIDI_BEND_DOWN);
2661 	showPushButton(PB_INST_EXT_MIDI_BEND_UP);
2662 
2663 	drawMIDICh();
2664 	drawMIDIPrg();
2665 	drawMIDIBend();
2666 }
2667 
showInstEditorExt(void)2668 void showInstEditorExt(void)
2669 {
2670 	if (ui.extended)
2671 		exitPatternEditorExtended();
2672 
2673 	hideTopScreen();
2674 	showTopScreen(false);
2675 
2676 	ui.instEditorExtShown = true;
2677 	ui.scopesShown = false;
2678 	drawInstEditorExt();
2679 }
2680 
hideInstEditorExt(void)2681 void hideInstEditorExt(void)
2682 {
2683 	hideScrollBar(SB_INST_EXT_MIDI_CH);
2684 	hideScrollBar(SB_INST_EXT_MIDI_PRG);
2685 	hideScrollBar(SB_INST_EXT_MIDI_BEND);
2686 	hideCheckBox(CB_INST_EXT_MIDI);
2687 	hideCheckBox(CB_INST_EXT_MUTE);
2688 	hidePushButton(PB_INST_EXT_MIDI_CH_DOWN);
2689 	hidePushButton(PB_INST_EXT_MIDI_CH_UP);
2690 	hidePushButton(PB_INST_EXT_MIDI_PRG_DOWN);
2691 	hidePushButton(PB_INST_EXT_MIDI_PRG_UP);
2692 	hidePushButton(PB_INST_EXT_MIDI_BEND_DOWN);
2693 	hidePushButton(PB_INST_EXT_MIDI_BEND_UP);
2694 
2695 	ui.instEditorExtShown = false;
2696 	ui.scopesShown = true;
2697 	drawScopeFramework();
2698 }
2699 
toggleInstEditorExt(void)2700 void toggleInstEditorExt(void)
2701 {
2702 	if (ui.instEditorExtShown)
2703 		hideInstEditorExt();
2704 	else
2705 		showInstEditorExt();
2706 }
2707 
testInstrSwitcherNormal(void)2708 static bool testInstrSwitcherNormal(void) // Welcome to the Jungle
2709 {
2710 	uint8_t newEntry;
2711 
2712 	if (mouse.x < 424 || mouse.x > 585)
2713 		return false;
2714 
2715 	if (mouse.y >= 5 && mouse.y <= 91)
2716 	{
2717 		// instruments
2718 		if (mouse.x >= 446 && mouse.x <= 584)
2719 		{
2720 			mouse.lastUsedObjectType = OBJECT_INSTRSWITCH;
2721 
2722 			if ((mouse.y-5) % 11 == 10)
2723 				return true; // we clicked on the one-pixel spacer
2724 
2725 			// destination instrument
2726 			newEntry = (editor.instrBankOffset + 1) + (uint8_t)((mouse.y - 5) / 11);
2727 			if (editor.curInstr != newEntry)
2728 			{
2729 				editor.curInstr = newEntry;
2730 				updateTextBoxPointers();
2731 				updateNewInstrument();
2732 			}
2733 
2734 			return true;
2735 		}
2736 		else if (mouse.x >= 424 && mouse.x <= 438)
2737 		{
2738 			mouse.lastUsedObjectType = OBJECT_INSTRSWITCH;
2739 
2740 			if ((mouse.y-5) % 11 == 10)
2741 				return true; // we clicked on the one-pixel spacer
2742 
2743 			// source isntrument
2744 			newEntry = (editor.instrBankOffset + 1) + (uint8_t)((mouse.y - 5) / 11);
2745 			if (editor.srcInstr != newEntry)
2746 			{
2747 				editor.srcInstr = newEntry;
2748 				updateInstrumentSwitcher();
2749 
2750 				if (ui.advEditShown)
2751 					updateAdvEdit();
2752 			}
2753 
2754 			return true;
2755 		}
2756 	}
2757 	else if (mouse.y >= 99 && mouse.y <= 152)
2758 	{
2759 		// samples
2760 		if (mouse.x >= 446 && mouse.x <= 560)
2761 		{
2762 			mouse.lastUsedObjectType = OBJECT_INSTRSWITCH;
2763 
2764 			if ((mouse.y-99) % 11 == 10)
2765 				return true; // we clicked on the one-pixel spacer
2766 
2767 			// destionation sample
2768 			newEntry = editor.sampleBankOffset + (uint8_t)((mouse.y - 99) / 11);
2769 			if (editor.curSmp != newEntry)
2770 			{
2771 				editor.curSmp = newEntry;
2772 				updateInstrumentSwitcher();
2773 				updateSampleEditorSample();
2774 
2775 				     if (ui.sampleEditorShown) updateSampleEditor();
2776 				else if (ui.instEditorShown)   updateInstEditor();
2777 			}
2778 
2779 			return true;
2780 		}
2781 		else if (mouse.x >= 423 && mouse.x <= 438)
2782 		{
2783 			mouse.lastUsedObjectType = OBJECT_INSTRSWITCH;
2784 
2785 			if ((mouse.y-99) % 11 == 10)
2786 				return true; // we clicked on the one-pixel spacer
2787 
2788 			// source sample
2789 			newEntry = editor.sampleBankOffset + (uint8_t)((mouse.y - 99) / 11);
2790 			if (editor.srcSmp != newEntry)
2791 			{
2792 				editor.srcSmp = newEntry;
2793 				updateInstrumentSwitcher();
2794 			}
2795 
2796 			return true;
2797 		}
2798 	}
2799 
2800 	return false;
2801 }
2802 
testInstrSwitcherExtended(void)2803 static bool testInstrSwitcherExtended(void) // Welcome to the Jungle 2 - The Happening
2804 {
2805 	uint8_t newEntry;
2806 
2807 	if (mouse.y < 5 || mouse.y > 47)
2808 		return false;
2809 
2810 	if (mouse.x >= 511)
2811 	{
2812 		// right columns
2813 		if (mouse.x <= 525)
2814 		{
2815 			mouse.lastUsedObjectType = OBJECT_INSTRSWITCH;
2816 
2817 			if ((mouse.y-5) % 11 == 10)
2818 				return true; // we clicked on the one-pixel spacer
2819 
2820 			// source instrument
2821 			newEntry = (editor.instrBankOffset + 5) + (uint8_t)((mouse.y - 5) / 11);
2822 			if (editor.srcInstr != newEntry)
2823 			{
2824 				editor.srcInstr = newEntry;
2825 				updateInstrumentSwitcher();
2826 
2827 				if (ui.advEditShown)
2828 					updateAdvEdit();
2829 			}
2830 
2831 			return true;
2832 		}
2833 		else if (mouse.x >= 529 && mouse.x <= 626)
2834 		{
2835 			mouse.lastUsedObjectType = OBJECT_INSTRSWITCH;
2836 
2837 			if ((mouse.y-5) % 11 == 10)
2838 				return true; // we clicked on the one-pixel spacer
2839 
2840 			// destination instrument
2841 			newEntry = (editor.instrBankOffset + 5) + (uint8_t)((mouse.y - 5) / 11);
2842 			if (editor.curInstr != newEntry)
2843 			{
2844 				editor.curInstr = newEntry;
2845 				updateTextBoxPointers();
2846 				updateNewInstrument();
2847 			}
2848 
2849 			return true;
2850 		}
2851 	}
2852 	else if (mouse.x >= 388)
2853 	{
2854 		// left columns
2855 		if (mouse.x <= 402)
2856 		{
2857 			mouse.lastUsedObjectType = OBJECT_INSTRSWITCH;
2858 
2859 			if ((mouse.y-5) % 11 == 10)
2860 				return true; // we clicked on the one-pixel spacer
2861 
2862 			// source instrument
2863 			newEntry = (editor.instrBankOffset + 1) + (uint8_t)((mouse.y - 5) / 11);
2864 			if (editor.srcInstr != newEntry)
2865 			{
2866 				editor.srcInstr = newEntry;
2867 				updateInstrumentSwitcher();
2868 
2869 				if (ui.advEditShown)
2870 					updateAdvEdit();
2871 			}
2872 
2873 			return true;
2874 		}
2875 		else if (mouse.x >= 406 && mouse.x <= 503)
2876 		{
2877 			mouse.lastUsedObjectType = OBJECT_INSTRSWITCH;
2878 
2879 			if ((mouse.y-5) % 11 == 10)
2880 				return true; // we clicked on the one-pixel spacer
2881 
2882 			// destination instrument
2883 			newEntry = (editor.instrBankOffset + 1) + (uint8_t)((mouse.y - 5) / 11);
2884 			if (editor.curInstr != newEntry)
2885 			{
2886 				editor.curInstr = newEntry;
2887 				updateTextBoxPointers();
2888 				updateNewInstrument();
2889 			}
2890 
2891 			return true;
2892 		}
2893 	}
2894 
2895 	return false;
2896 }
2897 
testInstrSwitcherMouseDown(void)2898 bool testInstrSwitcherMouseDown(void)
2899 {
2900 	if (!ui.instrSwitcherShown)
2901 		return false;
2902 
2903 	if (ui.extended)
2904 		return testInstrSwitcherExtended();
2905 	else
2906 		return testInstrSwitcherNormal();
2907 }
2908 
saveInstrThread(void * ptr)2909 static int32_t SDLCALL saveInstrThread(void *ptr)
2910 {
2911 	xiHdr_t ih;
2912 	sample_t *s;
2913 
2914 	if (editor.tmpFilenameU == NULL)
2915 	{
2916 		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?");
2917 		return false;
2918 	}
2919 
2920 	const int32_t numSamples = getUsedSamples(saveInstrNum);
2921 	if (numSamples == 0 || instr[saveInstrNum] == NULL)
2922 	{
2923 		okBoxThreadSafe(0, "System message", "Instrument is empty!");
2924 		return false;
2925 	}
2926 
2927 	FILE *f = UNICHAR_FOPEN(editor.tmpFilenameU, "wb");
2928 	if (f == NULL)
2929 	{
2930 		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?");
2931 		return false;
2932 	}
2933 
2934 	memset(&ih, 0, sizeof (ih)); // important, also clears reserved stuff
2935 
2936 	memcpy(ih.ID, "Extended Instrument: ", 21);
2937 	ih.version = 0x0102;
2938 
2939 	// song name
2940 	int32_t nameLength = (int32_t)strlen(song.instrName[saveInstrNum]);
2941 	if (nameLength > 22)
2942 		nameLength = 22;
2943 
2944 	memset(ih.name, ' ', 22); // yes, FT2 pads the name with spaces
2945 	if (nameLength > 0)
2946 		memcpy(ih.name, song.instrName[saveInstrNum], nameLength);
2947 
2948 	ih.name[22] = 0x1A;
2949 
2950 	// program/tracker name
2951 	nameLength = (int32_t)strlen(PROG_NAME_STR);
2952 	if (nameLength > 20)
2953 		nameLength = 20;
2954 
2955 	memset(ih.progName, ' ', 20); // yes, FT2 pads the name with spaces
2956 	if (nameLength > 0)
2957 		memcpy(ih.progName, PROG_NAME_STR, nameLength);
2958 
2959 	// copy over instrument struct data to instrument header
2960 	instr_t *ins = instr[saveInstrNum];
2961 	memcpy(ih.note2SampleLUT, ins->note2SampleLUT, 96);
2962 	memcpy(ih.volEnvPoints, ins->volEnvPoints, 12*2*sizeof(int16_t));
2963 	memcpy(ih.panEnvPoints, ins->panEnvPoints, 12*2*sizeof(int16_t));
2964 	ih.volEnvLength = ins->volEnvLength;
2965 	ih.panEnvLength = ins->panEnvLength;
2966 	ih.volEnvSustain = ins->volEnvSustain;
2967 	ih.volEnvLoopStart = ins->volEnvLoopStart;
2968 	ih.volEnvLoopEnd = ins->volEnvLoopEnd;
2969 	ih.panEnvSustain = ins->panEnvSustain;
2970 	ih.panEnvLoopStart = ins->panEnvLoopStart;
2971 	ih.panEnvLoopEnd = ins->panEnvLoopEnd;
2972 	ih.volEnvFlags = ins->volEnvFlags;
2973 	ih.panEnvFlags = ins->panEnvFlags;
2974 	ih.vibType = ins->vibType;
2975 	ih.vibSweep = ins->vibSweep;
2976 	ih.vibDepth = ins->vibDepth;
2977 	ih.vibRate = ins->vibRate;
2978 	ih.fadeout = ins->fadeout;
2979 	ih.midiOn = ins->midiOn ? 1 : 0;
2980 	ih.midiChannel = ins->midiChannel;
2981 	ih.midiProgram = ins->midiProgram;
2982 	ih.midiBend = ins->midiBend;
2983 	ih.mute = ins->mute ? 1 : 0;
2984 	ih.numSamples = (uint16_t)numSamples;
2985 
2986 	// copy over sample struct datas to sample headers
2987 	s = instr[saveInstrNum]->smp;
2988 	for (int32_t i = 0; i < numSamples; i++, s++)
2989 	{
2990 		xmSmpHdr_t *dst = &ih.smp[i];
2991 
2992 		bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
2993 
2994 		dst->length = s->length;
2995 		dst->loopStart = s->loopStart;
2996 		dst->loopLength = s->loopLength;
2997 
2998 		if (sample16Bit)
2999 		{
3000 			dst->length <<= 1;
3001 			dst->loopStart <<= 1;
3002 			dst->loopLength <<= 1;
3003 		}
3004 
3005 		dst->volume = s->volume;
3006 		dst->finetune = s->finetune;
3007 		dst->flags = s->flags;
3008 		dst->panning = s->panning;
3009 		dst->relativeNote = s->relativeNote;
3010 
3011 		dst->nameLength = (uint8_t)strlen(s->name);
3012 		if (dst->nameLength > 22)
3013 			dst->nameLength = 22;
3014 
3015 		memset(dst->name, 0, 22);
3016 		if (dst->nameLength > 0)
3017 			memcpy(dst->name, s->name, dst->nameLength);
3018 
3019 		if (s->dataPtr == NULL)
3020 			dst->length = 0;
3021 	}
3022 
3023 	size_t result = fwrite(&ih, INSTR_XI_HEADER_SIZE + (ih.numSamples * sizeof (xmSmpHdr_t)), 1, f);
3024 	if (result != 1)
3025 	{
3026 		fclose(f);
3027 		okBoxThreadSafe(0, "System message", "Error saving instrument: general I/O error!");
3028 		return false;
3029 	}
3030 
3031 	pauseAudio();
3032 	s = instr[saveInstrNum]->smp;
3033 	for (int32_t i = 0; i < numSamples; i++, s++)
3034 	{
3035 		if (s->dataPtr != NULL && s->length > 0)
3036 		{
3037 			unfixSample(s);
3038 			samp2Delta(s->dataPtr, s->length, s->flags);
3039 
3040 			result = fwrite(s->dataPtr, 1, SAMPLE_LENGTH_BYTES(s), f);
3041 
3042 			delta2Samp(s->dataPtr, s->length, s->flags);
3043 			fixSample(s);
3044 
3045 			if (result != (size_t)SAMPLE_LENGTH_BYTES(s)) // write not OK
3046 			{
3047 				resumeAudio();
3048 				fclose(f);
3049 				okBoxThreadSafe(0, "System message", "Error saving instrument: general I/O error!");
3050 				return false;
3051 			}
3052 		}
3053 	}
3054 	resumeAudio();
3055 
3056 	fclose(f);
3057 
3058 	editor.diskOpReadDir = true; // force diskop re-read
3059 	setMouseBusy(false);
3060 
3061 	return true;
3062 
3063 	(void)ptr;
3064 }
3065 
saveInstr(UNICHAR * filenameU,int16_t insNum)3066 void saveInstr(UNICHAR *filenameU, int16_t insNum)
3067 {
3068 	if (insNum == 0)
3069 		return;
3070 
3071 	saveInstrNum = insNum;
3072 	UNICHAR_STRCPY(editor.tmpFilenameU, filenameU);
3073 
3074 	mouseAnimOn();
3075 	thread = SDL_CreateThread(saveInstrThread, NULL, NULL);
3076 	if (thread == NULL)
3077 	{
3078 		okBox(0, "System message", "Couldn't create thread!");
3079 		return;
3080 	}
3081 
3082 	SDL_DetachThread(thread);
3083 }
3084 
getPATNote(int32_t freq)3085 static int16_t getPATNote(int32_t freq)
3086 {
3087 	const double dNote = (log2(freq / 440000.0) * 12.0) + 57.0;
3088 	const int32_t note = (const int32_t)(dNote + 0.5); // rounded
3089 
3090 	return (int16_t)note;
3091 }
3092 
loadInstrThread(void * ptr)3093 static int32_t SDLCALL loadInstrThread(void *ptr)
3094 {
3095 	int16_t a, b;
3096 	int32_t i, j, numLoadedSamples;
3097 	xiHdr_t xi_h;
3098 	patHdr_t pat_h;
3099 	patWaveHdr_t patWave_h;
3100 	xmSmpHdr_t *src;
3101 	sample_t *s;
3102 	instr_t *ins;
3103 
3104 	bool stereoWarning = false;
3105 	numLoadedSamples = 0;
3106 
3107 	if (editor.tmpInstrFilenameU == NULL)
3108 	{
3109 		okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
3110 		return false;
3111 	}
3112 
3113 	FILE *f = UNICHAR_FOPEN(editor.tmpInstrFilenameU, "rb");
3114 	if (f == NULL)
3115 	{
3116 		okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
3117 		return false;
3118 	}
3119 
3120 	memset(&xi_h, 0, sizeof (xi_h));
3121 	memset(&pat_h, 0, sizeof (pat_h));
3122 	memset(&patWave_h, 0, sizeof (patWave_h));
3123 
3124 	fread(&xi_h, INSTR_XI_HEADER_SIZE, 1, f);
3125 	if (!strncmp(xi_h.ID, "Extended Instrument: ", 21))
3126 	{
3127 		// XI - Extended Instrument
3128 
3129 		if (xi_h.version != 0x0101 && xi_h.version != 0x0102)
3130 		{
3131 			okBoxThreadSafe(0, "System message", "Incompatible format version!");
3132 			goto loadDone;
3133 		}
3134 
3135 		// not even FT2.01 can save old v1.01 .XI files, so I have no way to verify this
3136 		if (xi_h.version == 0x0101)
3137 		{
3138 			fseek(f, -20, SEEK_CUR);
3139 			xi_h.numSamples = xi_h.midiProgram;
3140 			xi_h.midiProgram = 0;
3141 			xi_h.midiBend = 0;
3142 			xi_h.mute = false;
3143 		}
3144 
3145 		numLoadedSamples = xi_h.numSamples;
3146 
3147 		memcpy(song.instrName[editor.curInstr], xi_h.name, 22);
3148 		song.instrName[editor.curInstr][22] = '\0';
3149 
3150 		pauseAudio();
3151 
3152 		freeInstr(editor.curInstr);
3153 
3154 		if (xi_h.numSamples > 0)
3155 		{
3156 			if (!allocateInstr(editor.curInstr))
3157 			{
3158 				resumeAudio();
3159 				okBoxThreadSafe(0, "System message", "Not enough memory!");
3160 				goto loadDone;
3161 			}
3162 
3163 			// copy instrument header elements to our instrument struct
3164 
3165 			ins = instr[editor.curInstr];
3166 			memcpy(ins->note2SampleLUT, xi_h.note2SampleLUT, 96);
3167 			memcpy(ins->volEnvPoints, xi_h.volEnvPoints, 12*2*sizeof(int16_t));
3168 			memcpy(ins->panEnvPoints, xi_h.panEnvPoints, 12*2*sizeof(int16_t));
3169 			ins->volEnvLength = xi_h.volEnvLength;
3170 			ins->panEnvLength = xi_h.panEnvLength;
3171 			ins->volEnvSustain = xi_h.volEnvSustain;
3172 			ins->volEnvLoopStart = xi_h.volEnvLoopStart;
3173 			ins->volEnvLoopEnd = xi_h.volEnvLoopEnd;
3174 			ins->panEnvSustain = xi_h.panEnvSustain;
3175 			ins->panEnvLoopStart = xi_h.panEnvLoopStart;
3176 			ins->panEnvLoopEnd = xi_h.panEnvLoopEnd;
3177 			ins->volEnvFlags = xi_h.volEnvFlags;
3178 			ins->panEnvFlags = xi_h.panEnvFlags;
3179 			ins->vibType = xi_h.vibType;
3180 			ins->vibSweep = xi_h.vibSweep;
3181 			ins->vibDepth = xi_h.vibDepth;
3182 			ins->vibRate = xi_h.vibRate;
3183 			ins->fadeout = xi_h.fadeout;
3184 			ins->midiOn = (xi_h.midiOn == 1) ? true : false;
3185 			ins->midiChannel = xi_h.midiChannel;
3186 			ins->midiProgram = xi_h.midiProgram;
3187 			ins->midiBend = xi_h.midiBend;
3188 			ins->mute = (xi_h.mute == 1) ? true : false; // correct logic! Don't mess with this
3189 			ins->numSamples = xi_h.numSamples; // used in loadInstrSample()
3190 
3191 			int32_t sampleHeadersToRead = xi_h.numSamples;
3192 			if (sampleHeadersToRead > MAX_SMP_PER_INST)
3193 				sampleHeadersToRead = MAX_SMP_PER_INST;
3194 
3195 			if (fread(xi_h.smp, sampleHeadersToRead * sizeof (xmSmpHdr_t), 1, f) != 1)
3196 			{
3197 				freeInstr(editor.curInstr);
3198 				resumeAudio();
3199 				okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
3200 				goto loadDone;
3201 			}
3202 
3203 			if (xi_h.numSamples > MAX_SMP_PER_INST)
3204 			{
3205 				const int32_t sampleHeadersToSkip = xi_h.numSamples - MAX_SMP_PER_INST;
3206 				fseek(f, sampleHeadersToSkip * sizeof (xmSmpHdr_t), SEEK_CUR);
3207 			}
3208 
3209 			for (i = 0; i < sampleHeadersToRead; i++)
3210 			{
3211 				s = &instr[editor.curInstr]->smp[i];
3212 				src = &xi_h.smp[i];
3213 
3214 				// copy sample header elements to our sample struct
3215 
3216 				s->length = src->length;
3217 				s->loopStart = src->loopStart;
3218 				s->loopLength = src->loopLength;
3219 				s->volume = src->volume;
3220 				s->finetune = src->finetune;
3221 				s->flags = src->flags;
3222 				s->panning = src->panning;
3223 				s->relativeNote = src->relativeNote;
3224 				memcpy(s->name, src->name, 22);
3225 				s->name[22] = '\0';
3226 
3227 				// dst->dataPtr is set up later
3228 			}
3229 		}
3230 
3231 		for (i = 0; i < xi_h.numSamples; i++)
3232 		{
3233 			s = &instr[editor.curInstr]->smp[i];
3234 
3235 			if (s->length <= 0)
3236 			{
3237 				s->length = 0;
3238 				s->loopStart = 0;
3239 				s->loopLength = 0;
3240 				s->flags = 0;
3241 			}
3242 			else
3243 			{
3244 				const int32_t lengthInFile = s->length;
3245 				bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
3246 				bool stereoSample = !!(s->flags & SAMPLE_STEREO);
3247 
3248 				if (sample16Bit) // we use units of samples (not bytes like in FT2)
3249 				{
3250 					s->length >>= 1;
3251 					s->loopStart >>= 1;
3252 					s->loopLength >>= 1;
3253 				}
3254 
3255 				if (s->length > MAX_SAMPLE_LEN)
3256 					s->length = MAX_SAMPLE_LEN;
3257 
3258 				if (!allocateSmpData(s, s->length, sample16Bit))
3259 				{
3260 					freeInstr(editor.curInstr);
3261 					resumeAudio();
3262 					okBoxThreadSafe(0, "System message", "Not enough memory!");
3263 					goto loadDone;
3264 				}
3265 
3266 				const int32_t sampleLengthInBytes = SAMPLE_LENGTH_BYTES(s);
3267 				if (fread(s->dataPtr, sampleLengthInBytes, 1, f) != 1)
3268 				{
3269 					freeInstr(editor.curInstr);
3270 					resumeAudio();
3271 					okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
3272 					goto loadDone;
3273 				}
3274 
3275 				if (sampleLengthInBytes < lengthInFile)
3276 					fseek(f, lengthInFile-sampleLengthInBytes, SEEK_CUR);
3277 
3278 				delta2Samp(s->dataPtr, s->length, s->flags); // stereo samples are handled here
3279 
3280 				// check if it was a stereo sample
3281 				if (stereoSample)
3282 				{
3283 					s->flags &= ~SAMPLE_STEREO;
3284 
3285 					s->length >>= 1;
3286 					s->loopStart >>= 1;
3287 					s->loopLength >>= 1;
3288 
3289 					reallocateSmpData(s, s->length, sample16Bit);
3290 					stereoWarning = true;
3291 				}
3292 			}
3293 		}
3294 
3295 		resumeAudio();
3296 	}
3297 	else
3298 	{
3299 		rewind(f);
3300 
3301 		fread(&pat_h, 1, sizeof (patHdr_t), f);
3302 		if (!memcmp(pat_h.ID, "GF1PATCH110\0ID#000002\0", 22))
3303 		{
3304 			// PAT - Gravis Ultrasound patch
3305 
3306 			if (pat_h.numSamples == 0)
3307 				pat_h.numSamples = 1; // to some patch makers, 0 means 1
3308 
3309 			if (pat_h.layers > 1 || pat_h.numSamples > MAX_SMP_PER_INST)
3310 			{
3311 				okBoxThreadSafe(0, "System message", "Incompatible instrument!");
3312 				goto loadDone;
3313 			}
3314 
3315 			numLoadedSamples = pat_h.numSamples;
3316 
3317 			pauseAudio();
3318 			freeInstr(editor.curInstr);
3319 
3320 			if (!allocateInstr(editor.curInstr))
3321 			{
3322 				okBoxThreadSafe(0, "System message", "Not enough memory!");
3323 				goto loadDone;
3324 			}
3325 
3326 			memcpy(song.instrName[editor.curInstr], pat_h.instrName, 16);
3327 			song.instrName[editor.curInstr][16] = '\0';
3328 
3329 			for (i = 0; i < pat_h.numSamples; i++)
3330 			{
3331 				s = &instr[editor.curInstr]->smp[i];
3332 				ins = instr[editor.curInstr];
3333 
3334 				if (fread(&patWave_h, 1, sizeof (patWave_h), f) != sizeof (patWave_h))
3335 				{
3336 					freeInstr(editor.curInstr);
3337 					resumeAudio();
3338 					okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
3339 					goto loadDone;
3340 				}
3341 
3342 				const int32_t lengthInFile = patWave_h.sampleLength;
3343 
3344 				bool sample16Bit = !!(patWave_h.flags & 1);
3345 
3346 				s->length = lengthInFile;
3347 				if (sample16Bit)
3348 				{
3349 					s->flags |= SAMPLE_16BIT;
3350 					s->length >>= 1;
3351 				}
3352 
3353 				if (s->length > MAX_SAMPLE_LEN)
3354 					s->length = MAX_SAMPLE_LEN;
3355 
3356 				if (!allocateSmpData(s, s->length, sample16Bit))
3357 				{
3358 					freeInstr(editor.curInstr);
3359 					resumeAudio();
3360 					okBoxThreadSafe(0, "System message", "Not enough memory!");
3361 					goto loadDone;
3362 				}
3363 
3364 				if (i == 0)
3365 				{
3366 					ins->vibSweep = patWave_h.vibSweep;
3367 					ins->vibRate = (patWave_h.vibRate + 2) >> 2; // rounded
3368 					ins->vibDepth = (patWave_h.vibDepth + 1) >> 1; // rounded
3369 				}
3370 
3371 				s = &instr[editor.curInstr]->smp[i];
3372 
3373 				memcpy(s->name, patWave_h.name, 7);
3374 				s->name[7] = '\0';
3375 
3376 				if (patWave_h.flags & 4) // loop enabled?
3377 				{
3378 					if (patWave_h.flags & 8)
3379 						s->flags |= LOOP_BIDI;
3380 					else
3381 						s->flags |= LOOP_FWD;
3382 				}
3383 
3384 				s->panning = ((patWave_h.panning << 4) & 0xF0) | (patWave_h.panning & 0xF);
3385 				s->loopStart = patWave_h.loopStart;
3386 				s->loopLength = patWave_h.loopEnd - patWave_h.loopStart;
3387 
3388 				if (sample16Bit)
3389 				{
3390 					s->loopStart >>= 1;
3391 					s->loopLength >>= 1;
3392 				}
3393 
3394 				const double dFreq = (1.0 + (patWave_h.finetune / 512.0)) * patWave_h.sampleRate;
3395 				tuneSample(s, (int32_t)(dFreq + 0.5), audio.linearPeriodsFlag);
3396 
3397 				a = getPATNote(patWave_h.rootFrq) - (12 * 3);
3398 				s->relativeNote -= (uint8_t)a;
3399 
3400 				a = getPATNote(patWave_h.lowFrq);
3401 				b = getPATNote(patWave_h.highFreq);
3402 
3403 				a = CLAMP(a, 0, 95);
3404 				b = CLAMP(b, 0, 95);
3405 
3406 				for (j = a; j <= b; j++)
3407 					ins->note2SampleLUT[j] = (uint8_t)i;
3408 
3409 				const int32_t sampleLengthInBytes = SAMPLE_LENGTH_BYTES(s);
3410 				size_t bytesRead = fread(s->dataPtr, sampleLengthInBytes, 1, f);
3411 				if (bytesRead != 1)
3412 				{
3413 					freeInstr(editor.curInstr);
3414 					resumeAudio();
3415 					okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
3416 					goto loadDone;
3417 				}
3418 
3419 				if (sampleLengthInBytes < lengthInFile)
3420 					fseek(f, lengthInFile-sampleLengthInBytes, SEEK_CUR);
3421 
3422 				if (patWave_h.flags & 2) // unsigned samples?
3423 				{
3424 					if (sample16Bit)
3425 						conv16BitSample(s->dataPtr, s->length, false);
3426 					else
3427 						conv8BitSample(s->dataPtr, s->length, false);
3428 				}
3429 			}
3430 
3431 			resumeAudio();
3432 		}
3433 	}
3434 
3435 loadDone:
3436 	fclose(f);
3437 
3438 	numLoadedSamples = CLAMP(numLoadedSamples, 1, MAX_SMP_PER_INST);
3439 
3440 	ins = instr[editor.curInstr];
3441 	if (ins != NULL)
3442 	{
3443 		sanitizeInstrument(ins);
3444 		for (i = 0; i < numLoadedSamples; i++)
3445 		{
3446 			s = &ins->smp[i];
3447 			sanitizeSample(s);
3448 
3449 			if (s->dataPtr != NULL)
3450 				fixSample(s);
3451 		}
3452 
3453 		fixInstrAndSampleNames(editor.curInstr);
3454 	}
3455 	editor.updateCurInstr = true; // setMouseBusy(false) is called in the input/video thread when done
3456 
3457 	if (numLoadedSamples > MAX_SMP_PER_INST)
3458 		okBoxThreadSafe(0, "System message", "Warning: The instrument contained >16 samples. The extra samples were discarded!");
3459 
3460 	if (stereoWarning)
3461 		okBoxThreadSafe(0, "System message", "Warning: The instrument contained stereo sample(s). They were mixed to mono!");
3462 
3463 	return true;
3464 	(void)ptr;
3465 }
3466 
fileIsInstr(UNICHAR * filenameU)3467 bool fileIsInstr(UNICHAR *filenameU)
3468 {
3469 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
3470 	if (f == NULL)
3471 		return false;
3472 
3473 	char header[22];
3474 	fread(header, 1, sizeof (header), f);
3475 	fclose(f);
3476 
3477 	if (!strncmp(header, "Extended Instrument: ", 21) || !memcmp(header, "GF1PATCH110\0ID#000002\0", 22))
3478 		return true;
3479 
3480 	return false;
3481 }
3482 
loadInstr(UNICHAR * filenameU)3483 void loadInstr(UNICHAR *filenameU)
3484 {
3485 	if (editor.curInstr == 0)
3486 	{
3487 		okBox(0, "System message", "The zero-instrument cannot hold intrument data.");
3488 		return;
3489 	}
3490 
3491 	UNICHAR_STRCPY(editor.tmpInstrFilenameU, filenameU);
3492 
3493 	if (fileIsInstr(filenameU))
3494 	{
3495 		// load as instrument
3496 		mouseAnimOn();
3497 		thread = SDL_CreateThread(loadInstrThread, NULL, NULL);
3498 		if (thread == NULL)
3499 		{
3500 			okBox(0, "System message", "Couldn't create thread!");
3501 			return;
3502 		}
3503 
3504 		SDL_DetachThread(thread);
3505 	}
3506 	else
3507 	{
3508 		// load as sample into sample slot #0 (and clear instrument)
3509 		loadSample(editor.tmpInstrFilenameU, 0, true);
3510 	}
3511 }
3512