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