1// MIT License
2
3// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
4
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23#include "sfx.h"
24#include "ext/history.h"
25
26#define DEFAULT_CHANNEL 0
27
28enum
29{
30 SFX_WAVE_PANEL,
31 SFX_VOLUME_PANEL,
32 SFX_CHORD_PANEL,
33 SFX_PITCH_PANEL,
34};
35
36static tic_sample* getEffect(Sfx* sfx)
37{
38 return sfx->src->samples.data + sfx->index;
39}
40
41static tic_waveform* getWaveformById(Sfx* sfx, s32 i)
42{
43 return &sfx->src->waveforms.items[i];
44}
45
46static void drawPanelBorder(tic_mem* tic, s32 x, s32 y, s32 w, s32 h, tic_color color)
47{
48 tic_api_rect(tic, x, y, w, h, color);
49
50 tic_api_rect(tic, x, y-1, w, 1, tic_color_dark_grey);
51 tic_api_rect(tic, x-1, y, 1, h, tic_color_dark_grey);
52 tic_api_rect(tic, x, y+h, w, 1, tic_color_light_grey);
53 tic_api_rect(tic, x+w, y, 1, h, tic_color_light_grey);
54}
55
56static s32 hold(Sfx* sfx, s32 value)
57{
58 tic_mem* tic = sfx->tic;
59
60 if(tic_api_key(tic, tic_key_ctrl) ||
61 tic_api_key(tic, tic_key_shift))
62 {
63 if(sfx->holdValue < 0)
64 sfx->holdValue = value;
65
66 return sfx->holdValue;
67 }
68
69 return value;
70}
71
72static inline void unhold(Sfx* sfx)
73{
74 sfx->holdValue = -1;
75}
76
77static void drawCanvasLeds(Sfx* sfx, s32 x, s32 y, s32 canvasTab)
78{
79 tic_mem* tic = sfx->tic;
80
81 enum
82 {
83 Cols = SFX_TICKS, Rows = 16,
84 Gap = 1, LedWidth = 3 + Gap, LedHeight = 1 + Gap,
85 Width = LedWidth * Cols + Gap,
86 Height = LedHeight * Rows + Gap
87 };
88
89 tic_api_rect(tic, x, y, Width, Height, tic_color_dark_grey);
90
91 for(s32 i = 0; i < Height; i += LedHeight)
92 tic_api_rect(tic, x, y + i, Width, Gap, tic_color_black);
93
94 for(s32 i = 0; i < Width; i += LedWidth)
95 tic_api_rect(tic, x + i, y, Gap, Height, tic_color_black);
96
97 {
98 const tic_sfx_pos* pos = &tic->ram->sfxpos[DEFAULT_CHANNEL];
99 s32 tickIndex = *(pos->data + canvasTab);
100
101 if(tickIndex >= 0)
102 tic_api_rect(tic, x + tickIndex * LedWidth, y, LedWidth + 1, Height, tic_color_white);
103 }
104
105 tic_rect rect = {x, y, Width - Gap, Height - Gap};
106
107 tic_sample* effect = getEffect(sfx);
108 tic_rect border = {-1};
109
110 if(checkMousePos(sfx->studio, &rect))
111 {
112 setCursor(sfx->studio, tic_cursor_hand);
113
114 s32 mx = tic_api_mouse(tic).x - x;
115 s32 my = tic_api_mouse(tic).y - y;
116 mx /= LedWidth;
117 s32 vy = my /= LedHeight;
118 border = (tic_rect){x + mx * LedWidth + Gap, y + my * LedHeight + Gap, LedWidth - Gap, LedHeight - Gap};
119
120 switch(canvasTab)
121 {
122 case SFX_VOLUME_PANEL: vy = MAX_VOLUME - my; break;
123 case SFX_WAVE_PANEL: sfx->hoverWave = vy = my = Rows - my - 1; break;
124 case SFX_CHORD_PANEL: vy = my = Rows - my - 1; break;
125 case SFX_PITCH_PANEL: vy = my = Rows / 2 - my - 1; break;
126 default: break;
127 }
128
129 SHOW_TOOLTIP(sfx->studio, "[x=%02i y=%02i]", mx, vy);
130
131 if(checkMouseDown(sfx->studio, &rect, tic_mouse_left))
132 {
133 my = hold(sfx, my);
134
135 switch(canvasTab)
136 {
137 case SFX_WAVE_PANEL: effect->data[mx].wave = my; break;
138 case SFX_VOLUME_PANEL: effect->data[mx].volume = my; break;
139 case SFX_CHORD_PANEL: effect->data[mx].chord = my; break;
140 case SFX_PITCH_PANEL: effect->data[mx].pitch = my; break;
141 default: break;
142 }
143
144 history_add(sfx->history);
145 }
146 else unhold(sfx);
147 }
148
149 for(s32 i = 0; i < Cols; i++)
150 {
151 switch(canvasTab)
152 {
153 case SFX_WAVE_PANEL:
154 for(s32 j = 1, start = Height - LedHeight, value = effect->data[i].wave + 1; j <= value; j++, start -= LedHeight)
155 tic_api_rect(tic, x + i * LedWidth + Gap, y + start, LedWidth-Gap, LedHeight-Gap, j == value ? tic_color_red : tic_color_orange);
156 break;
157
158 case SFX_VOLUME_PANEL:
159 for(s32 j = 1, start = Height - LedHeight, value = Rows - effect->data[i].volume; j <= value; j++, start -= LedHeight)
160 tic_api_rect(tic, x + i * LedWidth + Gap, y + start, LedWidth-Gap, LedHeight-Gap, j == value ? tic_color_blue : tic_color_light_blue);
161 break;
162
163 case SFX_CHORD_PANEL:
164 for(s32 j = 1, start = Height - LedHeight, value = effect->data[i].chord + 1; j <= value; j++, start -= LedHeight)
165 tic_api_rect(tic, x + i * LedWidth + Gap, y + start, LedWidth-Gap, LedHeight-Gap, j == value ? tic_color_green : tic_color_light_green);
166 break;
167
168 case SFX_PITCH_PANEL:
169 for(s32 value = effect->data[i].pitch, j = MIN(0, value); j <= MAX(0, value); j++)
170 tic_api_rect(tic, x + i * LedWidth + Gap, y + (Height / 2 - (j + 1) * LedHeight + Gap),
171 LedWidth-Gap, LedHeight-Gap, j == value ? tic_color_orange : tic_color_yellow);
172 break;
173 }
174 }
175
176 {
177 tic_sound_loop* loop = effect->loops + canvasTab;
178 if(loop->size > 0)
179 for(s32 r = 0; r < Rows; r++)
180 {
181 tic_api_rect(tic, x + loop->start * LedWidth + 2, y + Gap + r * LedHeight, 1, 1, tic_color_white);
182 tic_api_rect(tic, x + (loop->start + loop->size-1) * LedWidth + 2, y + Gap + r * LedHeight, 1, 1, tic_color_white);
183 }
184 }
185
186 if(border.x >= 0)
187 tic_api_rectb(tic, border.x, border.y, border.w, border.h, tic_color_white);
188}
189
190static void drawVolumeStereo(Sfx* sfx, s32 x, s32 y)
191{
192 tic_mem* tic = sfx->tic;
193
194 enum {Width = TIC_ALTFONT_WIDTH-1, Height = TIC_FONT_HEIGHT};
195
196 tic_sample* effect = getEffect(sfx);
197
198 {
199 tic_rect rect = {x, y, Width, Height};
200
201 bool hover = false;
202 if(checkMousePos(sfx->studio, &rect))
203 {
204 setCursor(sfx->studio, tic_cursor_hand);
205 hover = true;
206
207 showTooltip(sfx->studio, "left stereo");
208
209 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
210 effect->stereo_left = ~effect->stereo_left;
211 }
212
213 tic_api_print(tic, "L", rect.x, rect.y, effect->stereo_left ? hover ? tic_color_grey : tic_color_dark_grey : tic_color_light_green, true, 1, true);
214 }
215
216 {
217 tic_rect rect = {x + 4, y, Width, Height};
218
219 bool hover = false;
220 if(checkMousePos(sfx->studio, &rect))
221 {
222 setCursor(sfx->studio, tic_cursor_hand);
223 hover = true;
224
225 showTooltip(sfx->studio, "right stereo");
226
227 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
228 effect->stereo_right = ~effect->stereo_right;
229 }
230
231 tic_api_print(tic, "R", rect.x, rect.y, effect->stereo_right ? hover ? tic_color_grey : tic_color_dark_grey : tic_color_light_green, true, 1, true);
232 }
233}
234
235static void drawArppeggioSwitch(Sfx* sfx, s32 x, s32 y)
236{
237 tic_mem* tic = sfx->tic;
238
239 static const char Label[] = "DOWN";
240
241 enum {Width = (sizeof Label - 1) * TIC_ALTFONT_WIDTH - 1, Height = TIC_FONT_HEIGHT};
242
243 tic_sample* effect = getEffect(sfx);
244
245 {
246 tic_rect rect = {x, y, Width, Height};
247
248 bool hover = false;
249 if(checkMousePos(sfx->studio, &rect))
250 {
251 setCursor(sfx->studio, tic_cursor_hand);
252 hover = true;
253
254 showTooltip(sfx->studio, "up/down arpeggio");
255
256 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
257 effect->reverse = ~effect->reverse;
258 }
259
260 tic_api_print(tic, Label, rect.x, rect.y, effect->reverse ? tic_color_light_green : hover ? tic_color_grey : tic_color_dark_grey, true, 1, true);
261 }
262}
263
264static void drawPitchSwitch(Sfx* sfx, s32 x, s32 y)
265{
266 tic_mem* tic = sfx->tic;
267
268 static const char Label[] = "x16";
269
270 enum {Width = (sizeof Label - 1) * TIC_ALTFONT_WIDTH - 1, Height = TIC_FONT_HEIGHT};
271
272 tic_sample* effect = getEffect(sfx);
273
274 {
275 tic_rect rect = {x, y, Width, Height};
276
277 bool hover = false;
278 if(checkMousePos(sfx->studio, &rect))
279 {
280 setCursor(sfx->studio, tic_cursor_hand);
281 hover = true;
282
283 showTooltip(sfx->studio, "x16 pitch");
284
285 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
286 effect->pitch16x = ~effect->pitch16x;
287 }
288
289 tic_api_print(tic, Label, rect.x, rect.y, effect->pitch16x ? tic_color_light_green : hover ? tic_color_grey : tic_color_dark_grey, true, 1, true);
290 }
291}
292
293static void drawVolWaveSelector(Sfx* sfx, s32 x, s32 y)
294{
295 tic_mem* tic = sfx->tic;
296
297 typedef struct {const char* label; s32 panel; tic_rect rect; const char* tip;} Item;
298 static const Item Items[] =
299 {
300 {"WAV", SFX_WAVE_PANEL, {TIC_ALTFONT_WIDTH * 3, 0, TIC_ALTFONT_WIDTH * 3, TIC_FONT_HEIGHT}, "wave data"},
301 {"VOL", SFX_VOLUME_PANEL, {0, 0, TIC_ALTFONT_WIDTH * 3, TIC_FONT_HEIGHT}, "volume data"},
302 };
303
304 for(s32 i = 0; i < COUNT_OF(Items); i++)
305 {
306 const Item* item = &Items[i];
307
308 tic_rect rect = {x + item->rect.x, y + item->rect.y, item->rect.w, item->rect.h};
309
310 bool hover = false;
311
312 if(checkMousePos(sfx->studio, &rect))
313 {
314 showTooltip(sfx->studio, item->tip);
315
316 setCursor(sfx->studio, tic_cursor_hand);
317
318 hover = true;
319
320 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
321 sfx->volwave = item->panel;
322 }
323
324 tic_api_print(tic, item->label, x + item->rect.x, y + item->rect.y, item->panel == sfx->volwave ? tic_color_light_green : hover ? tic_color_grey : tic_color_dark_grey, true, 1, true);
325 }
326}
327
328static void drawCanvas(Sfx* sfx, s32 x, s32 y, s32 canvasTab)
329{
330 tic_mem* tic = sfx->tic;
331
332 enum
333 {
334 Width = 147, Height = 33
335 };
336
337 drawPanelBorder(tic, x, y, Width, Height, tic_color_black);
338
339 static const char* Labels[] = {"", "", "ARPEGG", "PITCH"};
340 tic_api_print(tic, Labels[canvasTab], x + 2, y + 2, tic_color_dark_grey, true, 1, true);
341
342 switch(canvasTab)
343 {
344 case SFX_WAVE_PANEL:
345 drawVolWaveSelector(sfx, x + 2, y + 2);
346 break;
347 case SFX_VOLUME_PANEL:
348 drawVolWaveSelector(sfx, x + 2, y + 2);
349 drawVolumeStereo(sfx, x + 2, y + 9);
350 break;
351 case SFX_CHORD_PANEL:
352 drawArppeggioSwitch(sfx, x + 2, y + 9);
353 break;
354 case SFX_PITCH_PANEL:
355 drawPitchSwitch(sfx, x + 2, y + 9);
356 break;
357 default:
358 break;
359 }
360
361 tic_api_print(tic, "LOOP:", x + 2, y + 20, tic_color_dark_grey, true, 1, true);
362
363 enum
364 {
365 ArrowWidth = 3, ArrowHeight = 5
366 };
367
368 tic_sample* effect = getEffect(sfx);
369 static const char SetLoopPosLabel[] = "set loop start";
370 static const char SetLoopSizeLabel[] = "set loop size";
371
372 {
373 tic_rect rect = {x + 2, y + 27, ArrowWidth, ArrowHeight};
374 bool hover = false;
375
376 if(checkMousePos(sfx->studio, &rect))
377 {
378 setCursor(sfx->studio, tic_cursor_hand);
379 hover = true;
380
381 showTooltip(sfx->studio, SetLoopPosLabel);
382
383 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
384 {
385 effect->loops[canvasTab].start--;
386 history_add(sfx->history);
387 }
388 }
389
390 drawBitIcon(sfx->studio, tic_icon_left, rect.x - 2, rect.y - 1, hover ? tic_color_grey : tic_color_dark_grey);
391 }
392
393 {
394 tic_rect rect = {x + 10, y + 27, ArrowWidth, ArrowHeight};
395 bool hover = false;
396
397 if(checkMousePos(sfx->studio, &rect))
398 {
399 setCursor(sfx->studio, tic_cursor_hand);
400 hover = true;
401
402 showTooltip(sfx->studio, SetLoopPosLabel);
403
404 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
405 {
406 effect->loops[canvasTab].start++;
407 history_add(sfx->history);
408 }
409 }
410
411 drawBitIcon(sfx->studio, tic_icon_right, rect.x - 2, rect.y - 1, hover ? tic_color_grey : tic_color_dark_grey);
412 }
413
414 {
415 char buf[] = "0";
416 sprintf(buf, "%X", effect->loops[canvasTab].start);
417 tic_api_print(tic, buf, x + 6, y + 27, tic_color_grey, true, 1, true);
418 }
419
420 {
421 tic_rect rect = {x + 14, y + 27, ArrowWidth, ArrowHeight};
422 bool hover = false;
423
424 if(checkMousePos(sfx->studio, &rect))
425 {
426 setCursor(sfx->studio, tic_cursor_hand);
427 hover = true;
428 showTooltip(sfx->studio, SetLoopSizeLabel);
429
430 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
431 {
432 effect->loops[canvasTab].size--;
433 history_add(sfx->history);
434 }
435 }
436
437 drawBitIcon(sfx->studio, tic_icon_left, rect.x - 2, rect.y - 1, hover ? tic_color_grey : tic_color_dark_grey);
438 }
439
440 {
441 tic_rect rect = {x + 22, y + 27, ArrowWidth, ArrowHeight};
442 bool hover = false;
443
444 if(checkMousePos(sfx->studio, &rect))
445 {
446 setCursor(sfx->studio, tic_cursor_hand);
447 hover = true;
448 showTooltip(sfx->studio, SetLoopSizeLabel);
449
450 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
451 {
452 effect->loops[canvasTab].size++;
453 history_add(sfx->history);
454 }
455 }
456
457 drawBitIcon(sfx->studio, tic_icon_right, rect.x - 2, rect.y - 1, hover ? tic_color_grey : tic_color_dark_grey);
458 }
459
460 {
461 char buf[] = "0";
462 sprintf(buf, "%X", effect->loops[canvasTab].size);
463 tic_api_print(tic, buf, x + 18, y + 27, tic_color_grey, true, 1, true);
464 }
465
466 drawCanvasLeds(sfx, x + 26, y, canvasTab);
467}
468
469static void playSound(Sfx* sfx)
470{
471 if(sfx->play.active)
472 {
473 tic_sample* effect = getEffect(sfx);
474
475 if(sfx->play.note != effect->note)
476 {
477 sfx->play.note = effect->note;
478
479 sfx_stop(sfx->tic, DEFAULT_CHANNEL);
480 tic_api_sfx(sfx->tic, sfx->index, effect->note, effect->octave, -1, DEFAULT_CHANNEL, MAX_VOLUME, MAX_VOLUME, SFX_DEF_SPEED);
481 }
482 }
483 else
484 {
485 sfx->play.note = -1;
486 sfx_stop(sfx->tic, DEFAULT_CHANNEL);
487 }
488}
489
490static void undo(Sfx* sfx)
491{
492 history_undo(sfx->history);
493}
494
495static void redo(Sfx* sfx)
496{
497 history_redo(sfx->history);
498}
499
500static void copyToClipboard(Sfx* sfx)
501{
502 tic_sample* effect = getEffect(sfx);
503 toClipboard(effect, sizeof(tic_sample), true);
504}
505
506static void resetSfx(Sfx* sfx)
507{
508 tic_sample* effect = getEffect(sfx);
509 memset(effect, 0, sizeof(tic_sample));
510
511 history_add(sfx->history);
512}
513
514static void cutToClipboard(Sfx* sfx)
515{
516 copyToClipboard(sfx);
517 resetSfx(sfx);
518}
519
520static void copyFromClipboard(Sfx* sfx)
521{
522 tic_sample* effect = getEffect(sfx);
523
524 if(fromClipboard(effect, sizeof(tic_sample), true, false, true))
525 history_add(sfx->history);
526}
527
528static void processKeyboard(Sfx* sfx)
529{
530 tic_mem* tic = sfx->tic;
531
532 if(tic->ram->input.keyboard.data == 0) return;
533
534 bool ctrl = tic_api_key(tic, tic_key_ctrl);
535
536 s32 keyboardButton = -1;
537
538 static const s32 Keycodes[] =
539 {
540 tic_key_z,
541 tic_key_s,
542 tic_key_x,
543 tic_key_d,
544 tic_key_c,
545 tic_key_v,
546 tic_key_g,
547 tic_key_b,
548 tic_key_h,
549 tic_key_n,
550 tic_key_j,
551 tic_key_m,
552 };
553
554 if(tic_api_key(tic, tic_key_alt))
555 return;
556
557 if(ctrl) {}
558 else
559 {
560 for(int i = 0; i < COUNT_OF(Keycodes); i++)
561 if(tic_api_key(tic, Keycodes[i]))
562 keyboardButton = i;
563 }
564
565 tic_sample* effect = getEffect(sfx);
566
567 if(keyboardButton >= 0)
568 {
569 effect->note = keyboardButton;
570 sfx->play.active = true;
571 }
572
573 if(tic_api_key(tic, tic_key_space))
574 sfx->play.active = true;
575}
576
577static void processEnvelopesKeyboard(Sfx* sfx)
578{
579 tic_mem* tic = sfx->tic;
580 bool ctrl = tic_api_key(tic, tic_key_ctrl);
581
582 switch(getClipboardEvent(sfx->studio))
583 {
584 case TIC_CLIPBOARD_CUT: cutToClipboard(sfx); break;
585 case TIC_CLIPBOARD_COPY: copyToClipboard(sfx); break;
586 case TIC_CLIPBOARD_PASTE: copyFromClipboard(sfx); break;
587 default: break;
588 }
589
590 if(ctrl)
591 {
592 if(keyWasPressed(sfx->studio, tic_key_z)) undo(sfx);
593 else if(keyWasPressed(sfx->studio, tic_key_y)) redo(sfx);
594 }
595
596 else if(keyWasPressed(sfx->studio, tic_key_left)) sfx->index--;
597 else if(keyWasPressed(sfx->studio, tic_key_right)) sfx->index++;
598 else if(keyWasPressed(sfx->studio, tic_key_delete)) resetSfx(sfx);
599}
600
601static tic_waveform* getWave(Sfx* sfx)
602{
603 tic_sample* effect = getEffect(sfx);
604 return getWaveformById(sfx, effect->data[0].wave);
605}
606
607static void copyWave(Sfx* sfx)
608{
609 toClipboard(getWave(sfx), sizeof(tic_waveform), true);
610}
611
612static void cutWave(Sfx* sfx)
613{
614 copyWave(sfx);
615
616 memset(getWave(sfx), 0, sizeof(tic_waveform));
617 history_add(sfx->waveHistory);
618}
619
620static void pasteWave(Sfx* sfx)
621{
622 if(fromClipboard(getWave(sfx), sizeof(tic_waveform), true, false, true))
623 history_add(sfx->waveHistory);
624}
625
626static void undoWave(Sfx* sfx)
627{
628 history_undo(sfx->waveHistory);
629}
630
631static void redoWave(Sfx* sfx)
632{
633 history_redo(sfx->waveHistory);
634}
635
636static void drawWavesBar(Sfx* sfx, s32 x, s32 y)
637{
638 static struct Button
639 {
640 u8 icon;
641 const char* tip;
642 void(*handler)(Sfx*);
643 } Buttons[] =
644 {
645 {
646 tic_icon_cut,
647 "CUT WAVE",
648 cutWave,
649 },
650 {
651 tic_icon_copy,
652 "COPY WAVE",
653 copyWave,
654 },
655 {
656 tic_icon_paste,
657 "PASTE WAVE",
658 pasteWave,
659 },
660 {
661 tic_icon_undo,
662 "UNDO WAVE",
663 undoWave,
664 },
665 {
666 tic_icon_redo,
667 "REDO WAVE",
668 redoWave,
669 },
670 };
671
672 enum {Size = 7};
673
674 FOR(const struct Button*, it, Buttons)
675 {
676 tic_rect rect = {x, y, Size, Size};
677
678 bool over = false;
679 s32 push = 0;
680 if(checkMousePos(sfx->studio, &rect))
681 {
682 over = true;
683 setCursor(sfx->studio, tic_cursor_hand);
684
685 showTooltip(sfx->studio, it->tip);
686
687 if(checkMouseDown(sfx->studio, &rect, tic_mouse_left))
688 push = 1;
689
690 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
691 it->handler(sfx);
692 }
693
694 if(over)
695 drawBitIcon(sfx->studio, it->icon, rect.x, rect.y + 1, tic_color_black);
696
697 drawBitIcon(sfx->studio, it->icon, rect.x, rect.y + push,
698 over ? tic_color_white : tic_color_dark_grey);
699
700 y += Size;
701 }
702}
703
704static void drawWaves(Sfx* sfx, s32 x, s32 y)
705{
706 tic_mem* tic = sfx->tic;
707
708 enum{Width = 10, Height = 6, MarginRight = 6, MarginBottom = 4, Cols = 4, Rows = 4, Scale = 4};
709
710 for(s32 i = 0; i < WAVES_COUNT; i++)
711 {
712 s32 xi = i % Cols;
713 s32 yi = i / Cols;
714
715 tic_rect rect = {x + xi * (Width + MarginRight), y + yi * (Height + MarginBottom), Width, Height};
716 tic_sample* effect = getEffect(sfx);
717 bool hover = false;
718
719 if(checkMousePos(sfx->studio, &rect))
720 {
721 setCursor(sfx->studio, tic_cursor_hand);
722
723 hover = true;
724
725 SHOW_TOOLTIP(sfx->studio, "select wave #%02i", i);
726
727 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
728 {
729 for(s32 c = 0; c < SFX_TICKS; c++)
730 effect->data[c].wave = i;
731
732 history_add(sfx->history);
733 }
734 }
735
736 bool sel = i == effect->data[0].wave;
737
738 const tic_sfx_pos* pos = &tic->ram->sfxpos[DEFAULT_CHANNEL];
739 bool active = *pos->data < 0 ? sfx->hoverWave == i : i == effect->data[*pos->data].wave;
740
741 drawPanelBorder(tic, rect.x, rect.y, rect.w, rect.h, active ? tic_color_orange : sel ? tic_color_light_green : tic_color_black);
742
743 // draw tiny wave previews
744 {
745 tic_waveform* wave = getWaveformById(sfx, i);
746
747 for(s32 i = 0; i < WAVE_VALUES/Scale; i++)
748 {
749 s32 value = tic_tool_peek4(wave->data, i*Scale)/Scale;
750 tic_api_pix(tic, rect.x + i+1, rect.y + Height - value - 2,
751 active ? tic_color_red : sel ? tic_color_dark_green : hover ? tic_color_light_grey : tic_color_white, false);
752 }
753
754 // draw flare
755 if(sel || active)
756 {
757 tic_api_rect(tic, rect.x + rect.w - 2, rect.y, 2, 1, tic_color_white);
758 tic_api_pix(tic, rect.x + rect.w - 1, rect.y + 1, tic_color_white, false);
759 }
760 }
761 }
762}
763
764static void drawWavePanel(Sfx* sfx, s32 x, s32 y)
765{
766 tic_mem* tic = sfx->tic;
767
768 enum {Width = 73, Height = 83, Round = 2};
769
770 typedef struct {s32 x; s32 y; s32 x1; s32 y1; tic_color color;} Edge;
771 static const Edge Edges[] =
772 {
773 {Width, Round, Width, Height - Round, tic_color_dark_grey},
774 {Round, Height, Width - Round, Height, tic_color_dark_grey},
775 {Width - Round, Height, Width, Height - Round, tic_color_dark_grey},
776 {Width - Round, 0, Width, Round, tic_color_dark_grey},
777 {0, Height - Round, Round, Height, tic_color_dark_grey},
778 {Round, 0, Width - Round, 0, tic_color_light_grey},
779 {0, Round, 0, Height - Round, tic_color_light_grey},
780 {0, Round, Round, 0, tic_color_white},
781 };
782
783 FOR(const Edge*, edge, Edges)
784 tic_api_line(tic, x + edge->x, y + edge->y, x + edge->x1, y + edge->y1, edge->color);
785
786 // draw current wave shape
787 {
788 enum {Scale = 2, MaxValue = WAVE_MAX_VALUE};
789
790 tic_rect rect = {x + 5, y + 5, 64, 32};
791 tic_sample* effect = getEffect(sfx);
792 tic_waveform* wave = getWaveformById(sfx, effect->data[0].wave);
793
794 drawPanelBorder(tic, rect.x - 1, rect.y - 1, rect.w + 2, rect.h + 2, tic_color_light_green);
795
796 if(sfx->play.active)
797 {
798 for(s32 i = 0; i < WAVE_VALUES; i++)
799 {
800 s32 amp = calcWaveAnimation(tic, i + sfx->play.tick, 0) / WAVE_MAX_VALUE;
801 tic_api_rect(tic, rect.x + i*Scale, rect.y + (MaxValue - amp) * Scale, Scale, Scale, tic_color_dark_green);
802 }
803 }
804 else
805 {
806 if(checkMousePos(sfx->studio, &rect))
807 {
808 setCursor(sfx->studio, tic_cursor_hand);
809
810 s32 cx = (tic_api_mouse(tic).x - rect.x) / Scale;
811 s32 cy = MaxValue - (tic_api_mouse(tic).y - rect.y) / Scale;
812
813 SHOW_TOOLTIP(sfx->studio, "[x=%02i y=%02i]", cx, cy);
814
815 enum {Border = 1};
816 tic_api_rectb(tic, rect.x + cx*Scale - Border,
817 rect.y + (MaxValue - cy) * Scale - Border, Scale + Border*2, Scale + Border*2, tic_color_dark_green);
818
819 if(checkMouseDown(sfx->studio, &rect, tic_mouse_left))
820 {
821 cy = hold(sfx, cy);
822
823 if(tic_tool_peek4(wave->data, cx) != cy)
824 {
825 tic_tool_poke4(wave->data, cx, cy);
826 history_add(sfx->waveHistory);
827 }
828 }
829 else unhold(sfx);
830 }
831
832 for(s32 i = 0; i < WAVE_VALUES; i++)
833 {
834 s32 value = tic_tool_peek4(wave->data, i);
835 tic_api_rect(tic, rect.x + i*Scale, rect.y + (MaxValue - value) * Scale, Scale, Scale, tic_color_dark_green);
836 }
837 }
838
839 // draw flare
840 {
841 tic_api_rect(tic, rect.x + 59, rect.y + 2, 4, 1, tic_color_white);
842 tic_api_rect(tic, rect.x + 62, rect.y + 2, 1, 3, tic_color_white);
843 }
844 }
845
846 drawWaves(sfx, x + 5, y + 43);
847 drawWavesBar(sfx, x + 65, y + 43);
848}
849
850static void drawPianoOctave(Sfx* sfx, s32 x, s32 y, s32 octave)
851{
852 tic_mem* tic = sfx->tic;
853
854 enum
855 {
856 Gap = 1, WhiteShadow = 1,
857 WhiteWidth = 3, WhiteHeight = 8, WhiteCount = 7, WhiteWidthGap = WhiteWidth + Gap,
858 BlackWidth = 3, BlackHeight = 4, BlackCount = 6,
859 BlackOffset = WhiteWidth - (BlackWidth - Gap) / 2,
860 Width = WhiteCount * WhiteWidthGap - Gap,
861 Height = WhiteHeight
862 };
863
864 tic_rect rect = {x, y, Width, Height};
865
866 typedef struct{s32 note; tic_rect rect; bool white;} PianoBtn;
867 static const PianoBtn Buttons[] =
868 {
869 {0, WhiteWidthGap * 0, 0, WhiteWidth, WhiteHeight, true},
870 {2, WhiteWidthGap * 1, 0, WhiteWidth, WhiteHeight, true},
871 {4, WhiteWidthGap * 2, 0, WhiteWidth, WhiteHeight, true},
872 {5, WhiteWidthGap * 3, 0, WhiteWidth, WhiteHeight, true},
873 {7, WhiteWidthGap * 4, 0, WhiteWidth, WhiteHeight, true},
874 {9, WhiteWidthGap * 5, 0, WhiteWidth, WhiteHeight, true},
875 {11, WhiteWidthGap * 6, 0, WhiteWidth, WhiteHeight, true},
876
877 {1, WhiteWidthGap * 0 + BlackOffset, 0, BlackWidth, BlackHeight, false},
878 {3, WhiteWidthGap * 1 + BlackOffset, 0, BlackWidth, BlackHeight, false},
879 {6, WhiteWidthGap * 3 + BlackOffset, 0, BlackWidth, BlackHeight, false},
880 {8, WhiteWidthGap * 4 + BlackOffset, 0, BlackWidth, BlackHeight, false},
881 {10, WhiteWidthGap * 5 + BlackOffset, 0, BlackWidth, BlackHeight, false},
882 };
883
884 tic_sample* effect = getEffect(sfx);
885
886 s32 hover = -1;
887
888 if(checkMousePos(sfx->studio, &rect))
889 {
890 for(s32 i = COUNT_OF(Buttons)-1; i >= 0; i--)
891 {
892 const PianoBtn* btn = Buttons + i;
893 tic_rect btnRect = btn->rect;
894 btnRect.x += x;
895 btnRect.y += y;
896
897 if(checkMousePos(sfx->studio, &btnRect))
898 {
899 setCursor(sfx->studio, tic_cursor_hand);
900
901 hover = btn->note;
902
903 {
904 static const char* Notes[] = SFX_NOTES;
905 SHOW_TOOLTIP(sfx->studio, "play %s%i note", Notes[btn->note], octave + 1);
906 }
907
908 if(checkMouseDown(sfx->studio, &rect, tic_mouse_left))
909 {
910 effect->note = btn->note;
911 effect->octave = octave;
912 sfx->play.active = true;
913
914 history_add(sfx->history);
915 }
916
917 break;
918 }
919 }
920 }
921
922 bool active = sfx->play.active && effect->octave == octave;
923
924 tic_api_rect(tic, rect.x, rect.y, rect.w, rect.h, tic_color_dark_grey);
925
926 for(s32 i = 0; i < COUNT_OF(Buttons); i++)
927 {
928 const PianoBtn* btn = Buttons + i;
929 const tic_rect* rect = &btn->rect;
930 tic_api_rect(tic, x + rect->x, y + rect->y, rect->w, rect->h,
931 active && effect->note == btn->note ? tic_color_red :
932 btn->white
933 ? hover == btn->note ? tic_color_light_grey : tic_color_white
934 : hover == btn->note ? tic_color_dark_grey : tic_color_black);
935
936 if(btn->white)
937 tic_api_rect(tic, x + rect->x, y + (WhiteHeight - WhiteShadow), WhiteWidth, WhiteShadow, tic_color_black);
938
939 // draw current note marker
940 if(effect->octave == octave && effect->note == btn->note)
941 tic_api_rect(tic, x + rect->x + 1, y + rect->y + rect->h - 3, 1, 1, tic_color_red);
942 }
943}
944
945static void drawPiano(Sfx* sfx, s32 x, s32 y)
946{
947 tic_mem* tic = sfx->tic;
948
949 enum {Width = 29};
950
951 for(s32 i = 0; i < OCTAVES; i++)
952 {
953 drawPianoOctave(sfx, x + Width*i, y, i);
954 }
955}
956
957static void drawSpeedPanel(Sfx* sfx, s32 x, s32 y)
958{
959 tic_mem* tic = sfx->tic;
960
961 enum
962 {
963 Count = 8, Gap = 1, ColWidth = 1, ColWidthGap = ColWidth + Gap,
964 Width = Count * ColWidthGap - Gap, Height = 5,
965 MaxSpeed = (1 << SFX_SPEED_BITS) / 2
966 };
967
968 tic_rect rect = {x + 13, y, Width, Height};
969 tic_sample* effect = getEffect(sfx);
970 s32 hover = -1;
971
972 if(checkMousePos(sfx->studio, &rect))
973 {
974 setCursor(sfx->studio, tic_cursor_hand);
975
976 s32 spd = (tic_api_mouse(tic).x - rect.x) / ColWidthGap;
977 hover = spd;
978
979 SHOW_TOOLTIP(sfx->studio, "set speed to %i", spd);
980
981 if(checkMouseDown(sfx->studio, &rect, tic_mouse_left))
982 {
983 effect->speed = spd - MaxSpeed;
984 history_add(sfx->history);
985 }
986 }
987
988 tic_api_print(tic, "SPD", x, y, tic_color_dark_grey, true, 1, true);
989
990 for(s32 i = 0; i < Count; i++)
991 tic_api_rect(tic, rect.x + i * ColWidthGap, rect.y, ColWidth, rect.h, i - MaxSpeed <= effect->speed ? tic_color_light_green : hover == i ? tic_color_grey : tic_color_dark_grey);
992}
993
994static void drawSelectorPanel(Sfx* sfx, s32 x, s32 y)
995{
996 tic_mem* tic = sfx->tic;
997
998 enum
999 {
1000 Size = 3, Gap = 1, SizeGap = Size + Gap,
1001 GroupGap = 2, Groups = 4, Cols = 4, Rows = SFX_COUNT / (Cols * Groups),
1002 GroupWidth = Cols * SizeGap - Gap,
1003 Width = (GroupWidth + GroupGap) * Groups - GroupGap, Height = Rows * SizeGap - Gap
1004 };
1005
1006 tic_rect rect = {x, y, Width, Height};
1007 s32 hover = -1;
1008
1009 if(checkMousePos(sfx->studio, &rect))
1010 for(s32 g = 0, i = 0; g < Groups; g++)
1011 for(s32 r = 0; r < Rows; r++)
1012 for(s32 c = 0; c < Cols; c++, i++)
1013 {
1014 tic_rect rect = {x + c * SizeGap + g * (GroupWidth + GroupGap), y + r * SizeGap, SizeGap, SizeGap};
1015
1016 if(checkMousePos(sfx->studio, &rect))
1017 {
1018 setCursor(sfx->studio, tic_cursor_hand);
1019 hover = i;
1020
1021 SHOW_TOOLTIP(sfx->studio, "edit sfx #%02i", hover);
1022
1023 if(checkMouseClick(sfx->studio, &rect, tic_mouse_left))
1024 sfx->index = i;
1025
1026 goto draw;
1027 }
1028 }
1029draw:
1030
1031 for(s32 g = 0, i = 0; g < Groups; g++)
1032 for(s32 r = 0; r < Rows; r++)
1033 for(s32 c = 0; c < Cols; c++, i++)
1034 {
1035 static const u8 EmptyEffect[sizeof(tic_sample)] = {0};
1036 bool empty = memcmp(sfx->src->samples.data + i, EmptyEffect, sizeof EmptyEffect) == 0;
1037
1038 tic_api_rect(tic, x + c * SizeGap + g * (GroupWidth + GroupGap), y + r * SizeGap, Size, Size,
1039 sfx->index == i ? tic_color_light_green : hover == i ? tic_color_grey : empty ? tic_color_dark_grey : tic_color_light_grey);
1040 }
1041}
1042
1043static void drawSelector(Sfx* sfx, s32 x, s32 y)
1044{
1045 tic_mem* tic = sfx->tic;
1046
1047 enum {Width = 70, Height = 25};
1048
1049 drawPanelBorder(tic, x, y, Width, Height, tic_color_black);
1050
1051 {
1052 char buf[] = "00";
1053 sprintf(buf, "%02i", sfx->index);
1054 tic_api_print(tic, buf, x + 20, y + 2, tic_color_light_green, true, 1, true);
1055 tic_api_print(tic, "IDX", x + 6, y + 2, tic_color_dark_grey, true, 1, true);
1056 }
1057
1058 drawSpeedPanel(sfx, x + 40, y + 2);
1059 drawSelectorPanel(sfx, x + 2, y + 9);
1060}
1061
1062static void tick(Sfx* sfx)
1063{
1064 tic_mem* tic = sfx->tic;
1065
1066 sfx->play.active = false;
1067 sfx->hoverWave = -1;
1068
1069 processKeyboard(sfx);
1070 processEnvelopesKeyboard(sfx);
1071
1072 tic_api_cls(tic, tic_color_grey);
1073
1074 drawCanvas(sfx, 88, 12, sfx->volwave);
1075 drawCanvas(sfx, 88, 51, SFX_CHORD_PANEL);
1076 drawCanvas(sfx, 88, 90, SFX_PITCH_PANEL);
1077
1078 drawSelector(sfx, 9, 12);
1079 drawPiano(sfx, 5, 127);
1080 drawWavePanel(sfx, 7, 41);
1081 drawToolbar(sfx->studio, tic, true);
1082
1083 playSound(sfx);
1084
1085 if(sfx->play.active)
1086 sfx->play.tick++;
1087 else
1088 sfx->play.tick = 0;
1089}
1090
1091static void onStudioEvent(Sfx* sfx, StudioEvent event)
1092{
1093 switch(event)
1094 {
1095 case TIC_TOOLBAR_CUT: cutToClipboard(sfx); break;
1096 case TIC_TOOLBAR_COPY: copyToClipboard(sfx); break;
1097 case TIC_TOOLBAR_PASTE: copyFromClipboard(sfx); break;
1098 case TIC_TOOLBAR_UNDO: undo(sfx); break;
1099 case TIC_TOOLBAR_REDO: redo(sfx); break;
1100 default: break;
1101 }
1102}
1103
1104void initSfx(Sfx* sfx, Studio* studio, tic_sfx* src)
1105{
1106 if(sfx->history) history_delete(sfx->history);
1107 if(sfx->waveHistory) history_delete(sfx->waveHistory);
1108
1109 *sfx = (Sfx)
1110 {
1111 .studio = studio,
1112 .tic = getMemory(studio),
1113 .tick = tick,
1114 .src = src,
1115 .index = 0,
1116 .volwave = SFX_VOLUME_PANEL,
1117 .hoverWave = -1,
1118 .holdValue = -1,
1119 .play =
1120 {
1121 .note = -1,
1122 .active = false,
1123 .tick = 0,
1124 },
1125
1126 .history = history_create(&src->samples, sizeof(tic_samples)),
1127 .waveHistory = history_create(&src->waveforms, sizeof(tic_waveforms)),
1128 .event = onStudioEvent,
1129 };
1130}
1131
1132void freeSfx(Sfx* sfx)
1133{
1134 history_delete(sfx->history);
1135 history_delete(sfx->waveHistory);
1136 free(sfx);
1137}