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 | |
28 | enum |
29 | { |
30 | SFX_WAVE_PANEL, |
31 | SFX_VOLUME_PANEL, |
32 | SFX_CHORD_PANEL, |
33 | SFX_PITCH_PANEL, |
34 | }; |
35 | |
36 | static tic_sample* getEffect(Sfx* sfx) |
37 | { |
38 | return sfx->src->samples.data + sfx->index; |
39 | } |
40 | |
41 | static tic_waveform* getWaveformById(Sfx* sfx, s32 i) |
42 | { |
43 | return &sfx->src->waveforms.items[i]; |
44 | } |
45 | |
46 | static 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 | |
56 | static 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 | |
72 | static inline void unhold(Sfx* sfx) |
73 | { |
74 | sfx->holdValue = -1; |
75 | } |
76 | |
77 | static 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 | |
190 | static 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 | |
235 | static 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 | |
264 | static 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 | |
293 | static 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 | |
328 | static 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 | |
469 | static 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 | |
490 | static void undo(Sfx* sfx) |
491 | { |
492 | history_undo(sfx->history); |
493 | } |
494 | |
495 | static void redo(Sfx* sfx) |
496 | { |
497 | history_redo(sfx->history); |
498 | } |
499 | |
500 | static void copyToClipboard(Sfx* sfx) |
501 | { |
502 | tic_sample* effect = getEffect(sfx); |
503 | toClipboard(effect, sizeof(tic_sample), true); |
504 | } |
505 | |
506 | static 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 | |
514 | static void cutToClipboard(Sfx* sfx) |
515 | { |
516 | copyToClipboard(sfx); |
517 | resetSfx(sfx); |
518 | } |
519 | |
520 | static 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 | |
528 | static 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 | |
577 | static 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 | |
601 | static tic_waveform* getWave(Sfx* sfx) |
602 | { |
603 | tic_sample* effect = getEffect(sfx); |
604 | return getWaveformById(sfx, effect->data[0].wave); |
605 | } |
606 | |
607 | static void copyWave(Sfx* sfx) |
608 | { |
609 | toClipboard(getWave(sfx), sizeof(tic_waveform), true); |
610 | } |
611 | |
612 | static void cutWave(Sfx* sfx) |
613 | { |
614 | copyWave(sfx); |
615 | |
616 | memset(getWave(sfx), 0, sizeof(tic_waveform)); |
617 | history_add(sfx->waveHistory); |
618 | } |
619 | |
620 | static void pasteWave(Sfx* sfx) |
621 | { |
622 | if(fromClipboard(getWave(sfx), sizeof(tic_waveform), true, false, true)) |
623 | history_add(sfx->waveHistory); |
624 | } |
625 | |
626 | static void undoWave(Sfx* sfx) |
627 | { |
628 | history_undo(sfx->waveHistory); |
629 | } |
630 | |
631 | static void redoWave(Sfx* sfx) |
632 | { |
633 | history_redo(sfx->waveHistory); |
634 | } |
635 | |
636 | static 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 | |
704 | static 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 | |
764 | static 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 | |
850 | static 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 | |
945 | static 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 | |
957 | static 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 | |
994 | static 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 | } |
1029 | draw: |
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 | |
1043 | static 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 | |
1062 | static 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 | |
1091 | static 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 | |
1104 | void 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 | |
1132 | void freeSfx(Sfx* sfx) |
1133 | { |
1134 | history_delete(sfx->history); |
1135 | history_delete(sfx->waveHistory); |
1136 | free(sfx); |
1137 | } |