1 /*
2 * Schism Tracker - a cross-platform Impulse Tracker clone
3 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5 * copyright (c) 2009 Storlek & Mrs. Brisby
6 * copyright (c) 2010-2012 Storlek
7 * URL: http://schismtracker.org/
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "headers.h"
25 #include "it.h"
26 #include "sndfile.h" /* for calc_halftone */
27 #include "page.h"
28 #include "song.h"
29 #include "dmoz.h"
30 #include "sample-edit.h"
31 #include "video.h"
32 #include "fmt.h"
33
34 /* --------------------------------------------------------------------- */
35 /* static in my attic */
36 static struct vgamem_overlay sample_image = {
37 55,26,76,29,
38 NULL, 0, 0, 0,
39 };
40
41 static int dialog_f1_hack = 0;
42
43 static struct widget widgets_samplelist[20];
44 static const int vibrato_waveforms[] = { 15, 16, 17, 18, -1 };
45
46 static int top_sample = 1;
47 static int current_sample = 1;
48 static int _altswap_lastvis = 99; // for alt-down sample-swapping
49
50 static int sample_list_cursor_pos = 25; /* the "play" text */
51
52 static void sample_adlibconfig_dialog(UNUSED void *ign);
53
54 /* shared by all the numentry widgets */
55 static int sample_numentry_cursor_pos = 0;
56
57 /* for the loops */
58 static const char *const loop_states[] = { "Off", "On Forwards", "On Ping Pong", NULL };
59
60 /* playback */
61 static int last_note = NOTE_MIDC;
62
63 static int num_save_formats = 0;
64
65 /* --------------------------------------------------------------------- */
66
67 /* woo */
68
_is_magic_sample(int no)69 static int _is_magic_sample(int no)
70 {
71 song_sample_t *sample;
72 int pn;
73
74 sample = song_get_sample(no);
75 if (sample && ((unsigned char) sample->name[23]) == 0xFF) {
76 pn = (sample->name[24]);
77 if (pn < 200) return 1;
78 }
79 return 0;
80 }
81
_fix_accept_text(void)82 static void _fix_accept_text(void)
83 {
84 if (_is_magic_sample(current_sample)) {
85 widgets_samplelist[0].accept_text = (sample_list_cursor_pos == 23 ? 0 : 1);
86 } else {
87 widgets_samplelist[0].accept_text = (sample_list_cursor_pos == 25 ? 0 : 1);
88 }
89 }
90
_last_vis_sample(void)91 static int _last_vis_sample(void)
92 {
93 int i, j, n;
94
95 n = 99;
96 j = 0;
97 /* 65 is first visible sample on last page */
98 for (i = 65; i < MAX_SAMPLES; i++) {
99 if (!csf_sample_is_empty(current_song->samples + i)) {
100 j = i;
101 }
102 }
103 while ((j + 34) > n) n += 34;
104 if (n >= MAX_SAMPLES) n = MAX_SAMPLES - 1;
105 return n;
106 }
107
108 /* --------------------------------------------------------------------- */
109
sample_list_reposition(void)110 static void sample_list_reposition(void)
111 {
112 if (current_sample < top_sample) {
113 top_sample = current_sample;
114 if (top_sample < 1)
115 top_sample = 1;
116 } else if (current_sample > top_sample + 34) {
117 top_sample = current_sample - 34;
118 }
119 if (dialog_f1_hack
120 && status.current_page == PAGE_SAMPLE_LIST
121 && status.previous_page == PAGE_HELP) {
122 sample_adlibconfig_dialog(NULL);
123 }
124 dialog_f1_hack = 0;
125 }
126
127 /* --------------------------------------------------------------------- */
128
sample_get_current(void)129 int sample_get_current(void)
130 {
131 return current_sample;
132 }
133
sample_set(int n)134 void sample_set(int n)
135 {
136 int new_sample = n;
137
138 if (status.current_page == PAGE_SAMPLE_LIST)
139 new_sample = CLAMP(n, 1, _last_vis_sample());
140 else
141 new_sample = CLAMP(n, 0, _last_vis_sample());
142
143 if (current_sample == new_sample)
144 return;
145
146 current_sample = new_sample;
147 sample_list_reposition();
148
149 /* update_current_instrument(); */
150 if (status.current_page == PAGE_SAMPLE_LIST)
151 status.flags |= NEED_UPDATE;
152 }
153
154 /* --------------------------------------------------------------------- */
155 /* draw the actual list */
156
sample_list_draw_list(void)157 static void sample_list_draw_list(void)
158 {
159 int pos, n, nl, pn;
160 song_sample_t *sample;
161 int has_data, is_selected;
162 char buf[64];
163 int ss, cl = 0, cr = 0;
164 int is_playing[MAX_SAMPLES];
165
166 ss = -1;
167
168 song_get_playing_samples(is_playing);
169
170 /* list */
171 for (pos = 0, n = top_sample; pos < 35; pos++, n++) {
172 sample = song_get_sample(n);
173 is_selected = (n == current_sample);
174 has_data = (sample->data != NULL);
175
176 if (sample->played)
177 draw_char(is_playing[n] > 1 ? 183 : 173, 1, 13 + pos, is_playing[n] ? 3 : 1, 2);
178
179 draw_text(num99tostr(n, buf), 2, 13 + pos, (sample->flags & CHN_MUTE) ? 1 : 0, 2);
180
181 // wow, this is entirely horrible
182 pn = ((unsigned char)sample->name[24]);
183 if (((unsigned char)sample->name[23]) == 0xFF && pn < 200) {
184 nl = 23;
185 draw_text(numtostr(3, (int)pn, buf), 32, 13 + pos, 0, 2);
186 draw_char('P', 28, 13+pos, 3, 2);
187 draw_char('a', 29, 13+pos, 3, 2);
188 draw_char('t', 30, 13+pos, 3, 2);
189 draw_char('.', 31, 13+pos, 3, 2);
190 } else {
191 nl = 25;
192 draw_char(168, 30, 13 + pos, 2, (is_selected ? 14 : 0));
193 draw_text("Play", 31, 13 + pos, (has_data ? 6 : 7), (is_selected ? 14 : 0));
194 }
195
196 draw_text_len(sample->name, nl, 5, 13 + pos, 6, (is_selected ? 14 : 0));
197 if (ss == n) {
198 draw_text_len(sample->name + cl, (cr-cl)+1, 5 + cl, 13 + pos, 3, 8);
199 }
200 }
201
202 /* cursor */
203 if (ACTIVE_PAGE.selected_widget == 0) {
204 pos = current_sample - top_sample;
205 sample = song_get_sample(current_sample);
206 has_data = (sample->data != NULL);
207
208 if (pos < 0 || pos > 34) {
209 /* err... */
210 } else if (sample_list_cursor_pos == 25) {
211 draw_text("Play", 31, 13 + pos, 0, (has_data ? 3 : 6));
212 } else {
213 draw_char(((sample_list_cursor_pos > (signed) strlen(sample->name))
214 ? 0 : sample->name[sample_list_cursor_pos]),
215 sample_list_cursor_pos + 5, 13 + pos, 0, 3);
216 }
217 }
218
219 status.flags |= NEED_UPDATE;
220 }
221
222 /* --------------------------------------------------------------------- */
223
sample_list_predraw_hook(void)224 static void sample_list_predraw_hook(void)
225 {
226 char buf[16];
227 song_sample_t *sample;
228 int has_data;
229
230 sample = song_get_sample(current_sample);
231 has_data = (sample->data != NULL);
232
233 /* set all the values to the current sample */
234
235 /* default volume
236 modplug hack here: sample volume has 4x the resolution...
237 can't deal with this in song.cc (easily) without changing the actual volume of the sample. */
238 widgets_samplelist[1].d.thumbbar.value = sample->volume / 4;
239
240 /* global volume */
241 widgets_samplelist[2].d.thumbbar.value = sample->global_volume;
242 widgets_samplelist[2].d.thumbbar.text_at_min = (sample->flags & CHN_MUTE) ? " Muted " : NULL;
243
244 /* default pan (another modplug hack) */
245 widgets_samplelist[3].d.toggle.state = (sample->flags & CHN_PANNING);
246 widgets_samplelist[4].d.thumbbar.value = sample->panning / 4;
247
248 widgets_samplelist[5].d.thumbbar.value = sample->vib_speed;
249 widgets_samplelist[6].d.thumbbar.value = sample->vib_depth;
250 widgets_samplelist[7].d.textentry.text = sample->filename;
251 widgets_samplelist[8].d.numentry.value = sample->c5speed;
252
253 widgets_samplelist[9].d.menutoggle.state =
254 (sample->flags & CHN_LOOP ? (sample->flags & CHN_PINGPONGLOOP ? 2 : 1) : 0);
255 widgets_samplelist[10].d.numentry.value = sample->loop_start;
256 widgets_samplelist[11].d.numentry.value = sample->loop_end;
257 widgets_samplelist[12].d.menutoggle.state =
258 (sample->flags & CHN_SUSTAINLOOP ? (sample->flags & CHN_PINGPONGSUSTAIN ? 2 : 1) : 0);
259 widgets_samplelist[13].d.numentry.value = sample->sustain_start;
260 widgets_samplelist[14].d.numentry.value = sample->sustain_end;
261
262 switch (sample->vib_type) {
263 case VIB_SINE:
264 togglebutton_set(widgets_samplelist, 15, 0);
265 break;
266 case VIB_RAMP_DOWN:
267 togglebutton_set(widgets_samplelist, 16, 0);
268 break;
269 case VIB_SQUARE:
270 togglebutton_set(widgets_samplelist, 17, 0);
271 break;
272 case VIB_RANDOM:
273 togglebutton_set(widgets_samplelist, 18, 0);
274 break;
275 }
276
277 widgets_samplelist[19].d.thumbbar.value = sample->vib_rate;
278
279 if (has_data) {
280 sprintf(buf, "%d bit%s",
281 (sample->flags & CHN_16BIT) ? 16 : 8,
282 (sample->flags & CHN_STEREO) ? " Stereo" : "");
283 } else {
284 strcpy(buf, "No sample");
285 }
286 draw_text_len(buf, 13, 64, 22, 2, 0);
287
288 draw_text_len(numtostr(0, sample->length, buf), 13, 64, 23, 2, 0);
289
290 draw_sample_data(&sample_image, sample);
291 }
292
293 /* --------------------------------------------------------------------- */
294
sample_list_add_char(char c)295 static int sample_list_add_char(char c)
296 {
297 song_sample_t *smp;
298
299 if (c < 32)
300 return 0;
301 smp = song_get_sample(current_sample);
302 text_add_char(smp->name, c, &sample_list_cursor_pos, _is_magic_sample(current_sample) ? 22 : 25);
303 _fix_accept_text();
304
305 status.flags |= NEED_UPDATE;
306 status.flags |= SONG_NEEDS_SAVE;
307 return 1;
308 }
309
sample_list_delete_char(void)310 static void sample_list_delete_char(void)
311 {
312 song_sample_t *smp = song_get_sample(current_sample);
313 text_delete_char(smp->name, &sample_list_cursor_pos, _is_magic_sample(current_sample) ? 23 : 25);
314 _fix_accept_text();
315
316 status.flags |= SONG_NEEDS_SAVE;
317 status.flags |= NEED_UPDATE;
318 }
319
sample_list_delete_next_char(void)320 static void sample_list_delete_next_char(void)
321 {
322 song_sample_t *smp = song_get_sample(current_sample);
323 text_delete_next_char(smp->name, &sample_list_cursor_pos, _is_magic_sample(current_sample) ? 23 : 25);
324 _fix_accept_text();
325
326 status.flags |= NEED_UPDATE;
327 status.flags |= SONG_NEEDS_SAVE;
328 }
329
clear_sample_text(void)330 static void clear_sample_text(void)
331 {
332 song_sample_t *smp = song_get_sample(current_sample);
333 memset(smp->filename, 0, 14);
334 if (_is_magic_sample(current_sample)) {
335 memset(smp->name, 0, 24);
336 } else {
337 memset(smp->name, 0, 26);
338 }
339 sample_list_cursor_pos = 0;
340 _fix_accept_text();
341
342 status.flags |= NEED_UPDATE;
343 status.flags |= SONG_NEEDS_SAVE;
344 }
345
346 /* --------------------------------------------------------------------- */
347
do_swap_sample(int n)348 static void do_swap_sample(int n)
349 {
350 if (n >= 1 && n <= _last_vis_sample()) {
351 song_swap_samples(current_sample, n);
352 }
353 }
354
do_exchange_sample(int n)355 static void do_exchange_sample(int n)
356 {
357 if (n >= 1 && n <= _last_vis_sample()) {
358 song_exchange_samples(current_sample, n);
359 }
360 }
361
do_copy_sample(int n)362 static void do_copy_sample(int n)
363 {
364 if (n >= 1 && n <= _last_vis_sample()) {
365 song_copy_sample(current_sample, song_get_sample(n));
366 sample_host_dialog(-1);
367 }
368 status.flags |= SONG_NEEDS_SAVE;
369 }
370
do_replace_sample(int n)371 static void do_replace_sample(int n)
372 {
373 if (n >= 1 && n <= _last_vis_sample()) {
374 song_replace_sample(current_sample, n);
375 }
376 status.flags |= SONG_NEEDS_SAVE;
377 }
378
379 /* --------------------------------------------------------------------- */
380
sample_list_handle_key_on_list(struct key_event * k)381 static int sample_list_handle_key_on_list(struct key_event * k)
382 {
383 int new_sample = current_sample;
384 int new_cursor_pos = sample_list_cursor_pos;
385
386 if (k->mouse == MOUSE_CLICK && k->mouse_button == MOUSE_BUTTON_MIDDLE) {
387 if (k->state == KEY_RELEASE)
388 status.flags |= CLIPPY_PASTE_SELECTION;
389 return 1;
390 } else if (k->state == KEY_PRESS && k->mouse != MOUSE_NONE && k->x >= 5 && k->y >= 13 && k->y <= 47 && k->x <= 34) {
391 if (k->mouse == MOUSE_SCROLL_UP) {
392 top_sample -= MOUSE_SCROLL_LINES;
393 if (top_sample < 1) top_sample = 1;
394 status.flags |= NEED_UPDATE;
395 return 1;
396 } else if (k->mouse == MOUSE_SCROLL_DOWN) {
397 top_sample += MOUSE_SCROLL_LINES;
398 if (top_sample > (_last_vis_sample()-34))
399 top_sample = (_last_vis_sample()-34);
400 status.flags |= NEED_UPDATE;
401 return 1;
402 } else {
403 new_sample = (k->y - 13) + top_sample;
404 new_cursor_pos = k->x - 5;
405 if (k->x <= 29) { /* and button1 */
406 if (k->mouse == MOUSE_DBLCLICK) {
407 /* this doesn't appear to work */
408 set_page(PAGE_LOAD_SAMPLE);
409 status.flags |= NEED_UPDATE;
410 return 1;
411 } else {
412 }
413 #if 0 /* buggy and annoying, could be implemented properly but I don't care enough */
414 } else if (k->state == KEY_RELEASE || k->x == k->sx) {
415 if (k->mouse == MOUSE_DBLCLICK
416 || (new_sample == current_sample
417 && sample_list_cursor_pos == 25)) {
418 song_keydown(current_sample, KEYJAZZ_NOINST,
419 last_note, 64, KEYJAZZ_CHAN_CURRENT);
420 }
421 new_cursor_pos = 25;
422 #endif
423 }
424 }
425 } else {
426 switch (k->sym) {
427 case SDLK_LEFT:
428 if (k->state == KEY_RELEASE)
429 return 0;
430 if (!NO_MODIFIER(k->mod))
431 return 0;
432 new_cursor_pos--;
433 break;
434 case SDLK_RIGHT:
435 if (k->state == KEY_RELEASE)
436 return 0;
437 if (!NO_MODIFIER(k->mod))
438 return 0;
439 new_cursor_pos++;
440 break;
441 case SDLK_HOME:
442 if (k->state == KEY_RELEASE)
443 return 0;
444 if (!NO_MODIFIER(k->mod))
445 return 0;
446 new_cursor_pos = 0;
447 break;
448 case SDLK_END:
449 if (k->state == KEY_RELEASE)
450 return 0;
451 if (!NO_MODIFIER(k->mod))
452 return 0;
453 new_cursor_pos = 25;
454 break;
455 case SDLK_UP:
456 if (k->state == KEY_RELEASE)
457 return 0;
458 if (k->mod & KMOD_ALT) {
459 if (current_sample > 1) {
460 new_sample = current_sample - 1;
461 song_swap_samples(current_sample, new_sample);
462 }
463 } else if (!NO_MODIFIER(k->mod)) {
464 return 0;
465 } else {
466 new_sample--;
467 }
468 break;
469 case SDLK_DOWN:
470 if (k->state == KEY_RELEASE)
471 return 0;
472 if (k->mod & KMOD_ALT) {
473 // restrict position to the "old" value of _last_vis_sample()
474 // (this is entirely for aesthetic reasons)
475 if (status.last_keysym != SDLK_DOWN && !k->is_repeat)
476 _altswap_lastvis = _last_vis_sample();
477 if (current_sample < _altswap_lastvis) {
478 new_sample = current_sample + 1;
479 song_swap_samples(current_sample, new_sample);
480 }
481 } else if (!NO_MODIFIER(k->mod)) {
482 return 0;
483 } else {
484 new_sample++;
485 }
486 break;
487 case SDLK_PAGEUP:
488 if (k->state == KEY_RELEASE)
489 return 0;
490 if (k->mod & KMOD_CTRL) {
491 new_sample = 1;
492 } else {
493 new_sample -= 16;
494 }
495 break;
496 case SDLK_PAGEDOWN:
497 if (k->state == KEY_RELEASE)
498 return 0;
499 if (k->mod & KMOD_CTRL) {
500 new_sample = _last_vis_sample();
501 } else {
502 new_sample += 16;
503 }
504 break;
505 case SDLK_RETURN:
506 if (k->state == KEY_PRESS)
507 return 0;
508 set_page(PAGE_LOAD_SAMPLE);
509 break;
510 case SDLK_BACKSPACE:
511 if (k->state == KEY_RELEASE)
512 return 0;
513 if ((k->mod & (KMOD_CTRL | KMOD_ALT)) == 0) {
514 if (sample_list_cursor_pos < 25) {
515 sample_list_delete_char();
516 }
517 return 1;
518 } else if (k->mod & KMOD_CTRL) {
519 /* just for compatibility with every weird thing
520 * Impulse Tracker does ^_^ */
521 if (sample_list_cursor_pos < 25) {
522 sample_list_add_char(127);
523 }
524 return 1;
525 }
526 return 0;
527 case SDLK_DELETE:
528 if (k->state == KEY_RELEASE)
529 return 0;
530 if ((k->mod & (KMOD_CTRL | KMOD_ALT)) == 0) {
531 if (sample_list_cursor_pos < 25) {
532 sample_list_delete_next_char();
533 }
534 return 1;
535 }
536 return 0;
537 case SDLK_ESCAPE:
538 if (k->mod & KMOD_SHIFT) {
539 if (k->state == KEY_RELEASE)
540 return 1;
541 new_cursor_pos = 25;
542 break;
543 }
544 return 0;
545 default:
546 if (k->mod & KMOD_ALT) {
547 if (k->sym == SDLK_c) {
548 clear_sample_text();
549 return 1;
550 }
551 } else if ((k->mod & KMOD_CTRL) == 0 && sample_list_cursor_pos < 25) {
552 if (!k->unicode) return 0;
553 if (k->state == KEY_RELEASE)
554 return 1;
555 return sample_list_add_char(k->unicode);
556 }
557 return 0;
558 }
559 }
560
561 new_sample = CLAMP(new_sample, 1, _last_vis_sample());
562 new_cursor_pos = CLAMP(new_cursor_pos, 0, 25);
563
564 if (new_sample != current_sample) {
565 sample_set(new_sample);
566 sample_list_reposition();
567 }
568 if (new_cursor_pos != sample_list_cursor_pos) {
569 sample_list_cursor_pos = new_cursor_pos;
570 _fix_accept_text();
571 }
572
573 status.flags |= NEED_UPDATE;
574 return 1;
575 }
576
577 /* --------------------------------------------------------------------- */
578 /* alt key dialog callbacks.
579 * these don't need to do any actual redrawing, because the screen gets
580 * redrawn anyway when the dialog is cleared. */
581
do_sign_convert(UNUSED void * data)582 static void do_sign_convert(UNUSED void *data)
583 {
584 song_sample_t *sample = song_get_sample(current_sample);
585 sample_sign_convert(sample);
586 }
587
do_quality_convert(UNUSED void * data)588 static void do_quality_convert(UNUSED void *data)
589 {
590 song_sample_t *sample = song_get_sample(current_sample);
591 sample_toggle_quality(sample, 1);
592 }
593
do_quality_toggle(UNUSED void * data)594 static void do_quality_toggle(UNUSED void *data)
595 {
596 song_sample_t *sample = song_get_sample(current_sample);
597
598 if (sample->flags & CHN_STEREO)
599 status_text_flash("Can't toggle quality for stereo samples");
600 else
601 sample_toggle_quality(sample, 0);
602 }
603
do_delete_sample(UNUSED void * data)604 static void do_delete_sample(UNUSED void *data)
605 {
606 song_clear_sample(current_sample);
607 status.flags |= SONG_NEEDS_SAVE;
608 }
609
do_downmix(UNUSED void * data)610 static void do_downmix(UNUSED void *data)
611 {
612 song_sample_t *sample = song_get_sample(current_sample);
613 sample_downmix(sample);
614 }
615
do_post_loop_cut(UNUSED void * bweh)616 static void do_post_loop_cut(UNUSED void *bweh) /* I'm already using 'data'. */
617 {
618 song_sample_t *sample = song_get_sample(current_sample);
619 unsigned long pos = ((sample->flags & CHN_SUSTAINLOOP)
620 ? MAX(sample->loop_end, sample->sustain_end)
621 : sample->loop_end);
622
623 if (pos == 0 || pos >= sample->length)
624 return;
625
626 status.flags |= SONG_NEEDS_SAVE;
627
628 song_lock_audio();
629 csf_stop_sample(current_song, sample);
630 if (sample->loop_end > pos) sample->loop_end = pos;
631 if (sample->sustain_end > pos) sample->sustain_end = pos;
632
633 sample->length = pos;
634 csf_adjust_sample_loop(sample);
635 song_unlock_audio();
636 }
637
do_pre_loop_cut(UNUSED void * bweh)638 static void do_pre_loop_cut(UNUSED void *bweh)
639 {
640 song_sample_t *sample = song_get_sample(current_sample);
641 unsigned long pos = ((sample->flags & CHN_SUSTAINLOOP)
642 ? MIN(sample->loop_start, sample->sustain_start)
643 : sample->loop_start);
644 unsigned long start_byte = pos * ((sample->flags & CHN_16BIT) ? 2 : 1)
645 * ((sample->flags & CHN_STEREO) ? 2 : 1);
646 unsigned long bytes = (sample->length - pos) * ((sample->flags & CHN_16BIT) ? 2 : 1)
647 * ((sample->flags & CHN_STEREO) ? 2 : 1);
648
649 if (pos == 0 || pos > sample->length)
650 return;
651
652 status.flags |= SONG_NEEDS_SAVE;
653
654 song_lock_audio();
655 csf_stop_sample(current_song, sample);
656 memmove(sample->data, sample->data + start_byte, bytes);
657 sample->length -= pos;
658
659 if (sample->loop_start > pos)
660 sample->loop_start -= pos;
661 else
662 sample->loop_start = 0;
663 if (sample->sustain_start > pos)
664 sample->sustain_start -= pos;
665 else
666 sample->sustain_start = 0;
667 if (sample->loop_end > pos)
668 sample->loop_end -= pos;
669 else
670 sample->loop_end = 0;
671 if (sample->sustain_end > pos)
672 sample->sustain_end -= pos;
673 else
674 sample->sustain_end = 0;
675 song_unlock_audio();
676 }
677
do_centralise(UNUSED void * data)678 static void do_centralise(UNUSED void *data)
679 {
680 song_sample_t *sample = song_get_sample(current_sample);
681 sample_centralise(sample);
682 }
683
684 /* --------------------------------------------------------------------- */
685
686 static struct widget sample_amplify_widgets[3];
687
do_amplify(UNUSED void * data)688 static void do_amplify(UNUSED void *data)
689 {
690 sample_amplify(song_get_sample(current_sample), sample_amplify_widgets[0].d.thumbbar.value);
691 }
692
sample_amplify_draw_const(void)693 static void sample_amplify_draw_const(void)
694 {
695 draw_text("Sample Amplification %", 29, 27, 0, 2);
696 draw_box(12, 29, 64, 31, BOX_THIN | BOX_INNER | BOX_INSET);
697 }
698
sample_amplify_dialog(void)699 static void sample_amplify_dialog(void)
700 {
701 struct dialog *dialog;
702 int percent = sample_get_amplify_amount(song_get_sample(current_sample));
703
704 percent = MIN(percent, 400);
705
706 create_thumbbar(sample_amplify_widgets + 0, 13, 30, 51, 0, 1, 1, NULL, 0, 400);
707 sample_amplify_widgets[0].d.thumbbar.value = percent;
708 create_button(sample_amplify_widgets + 1, 31, 33, 6, 0, 1, 2, 2, 2, dialog_yes_NULL, "OK", 3);
709 create_button(sample_amplify_widgets + 2, 41, 33, 6, 0, 2, 1, 1, 1, dialog_cancel_NULL, "Cancel", 1);
710
711 dialog = dialog_create_custom(9, 25, 61, 11, sample_amplify_widgets,
712 3, 0, sample_amplify_draw_const, NULL);
713 dialog->action_yes = do_amplify;
714 }
715
716 /* --------------------------------------------------------------------- */
717
718 static struct widget txtsynth_widgets[3];
719 static char txtsynth_entry[65536];
720
do_txtsynth(UNUSED void * data)721 static void do_txtsynth(UNUSED void *data)
722 {
723 int len = strlen(txtsynth_entry);
724 if (!len)
725 return;
726
727 song_sample_t *sample = song_get_sample(current_sample);
728 if (sample->data)
729 csf_free_sample(sample->data);
730 sample->data = csf_allocate_sample(len);
731 memcpy(sample->data, txtsynth_entry, len);
732 sample->length = len;
733 sample->loop_start = 0;
734 sample->loop_end = len;
735 sample->sustain_start = sample->sustain_end = 0;
736 sample->flags |= CHN_LOOP;
737 sample->flags &= ~(CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN
738 | CHN_16BIT | CHN_STEREO | CHN_ADLIB);
739 csf_adjust_sample_loop(sample);
740 sample_host_dialog(-1);
741
742 status.flags |= SONG_NEEDS_SAVE;
743 }
744
txtsynth_draw_const(void)745 static void txtsynth_draw_const(void)
746 {
747 draw_text("Enter a text string (e.g. ABCDCB for a triangle-wave)", 13, 27, 0, 2);
748 draw_box(12, 29, 66, 31, BOX_THIN | BOX_INNER | BOX_INSET);
749 }
750
txtsynth_dialog(void)751 static void txtsynth_dialog(void)
752 {
753 struct dialog *dialog;
754
755 // TODO copy the current sample into the entry?
756
757 txtsynth_entry[0] = 0;
758 create_textentry(txtsynth_widgets + 0, 13, 30, 53, 0, 1, 1, NULL, txtsynth_entry, 65535);
759 create_button(txtsynth_widgets + 1, 31, 33, 6, 0, 1, 2, 2, 2, dialog_yes_NULL, "OK", 3);
760 create_button(txtsynth_widgets + 2, 41, 33, 6, 0, 2, 1, 1, 1, dialog_cancel_NULL, "Cancel", 1);
761
762 dialog = dialog_create_custom(9, 25, 61, 11, txtsynth_widgets, 3, 0, txtsynth_draw_const, NULL);
763 dialog->action_yes = do_txtsynth;
764 }
765
766 /* --------------------------------------------------------------------- */
767
768 static struct widget sample_adlibconfig_widgets[28];
769 static int adlib_xpos[] = {26, 30, 58, 62, 39};
770 static int adlib_cursorpos[] = {0, 0, 0, 0, 0};
771 static const char *yn_toggle[3] = {"n", "y", NULL};
772
773 /* N - number, B - boolean (toggle) */
774 typedef enum adlibconfig_wtypes {N, B} adlibconfig_wtypes;
775 static const struct {
776 int xref, y;
777 adlibconfig_wtypes type;
778 int byteno, firstbit, nbits;
779 } adlibconfig_widgets[] = {
780 {4, 3, B,10, 0, 1 }, // add. synth
781 {4, 4, N,10, 1, 3 }, // mod. feedback
782
783 {0, 7, N, 5, 4, 4 }, // carrier attack
784 {0, 8, N, 5, 0, 4 }, // carrier decay
785 {0, 9, N, 7, 4,-4 }, // carrier sustain (0=maximum, 15=minimum)
786 {0, 10, N, 7, 0, 4 }, // carrier release
787 {0, 11, B, 1, 5, 1 }, // carrier sustain flag
788 {0, 12, N, 3, 0,-6 }, // carrier volume (0=maximum, 63=minimum)
789
790 {1, 7, N, 4, 4, 4 }, // modulator attack
791 {1, 8, N, 4, 0, 4 }, // modulator decay
792 {1, 9, N, 6, 4,-4 }, // modulator sustain (0=maximum, 15=minimum)
793 {1, 10, N, 6, 0, 4 }, // modulator release
794 {1, 11, B, 0, 5, 1 }, // modulator sustain flag
795 {1, 12, N, 2, 0,-6 }, // modulator volume (0=maximum, 63=minimum)
796
797 {2, 7, B, 1, 4, 1 }, // carrier scale envelope flag
798 {2, 8, N, 3, 6, 2 }, // carrier level scaling (This is actually reversed bits...)
799 {2, 9, N, 1, 0, 4 }, // carrier frequency multiplier
800 {2, 10, N, 9, 0, 3 }, // carrier wave select
801 {2, 11, B, 1, 6, 1 }, // carrier pitch vibrato
802 {2, 12, B, 1, 7, 1 }, // carrier volume vibrato
803
804 {3, 7, B, 0, 4, 1 }, // modulator scale envelope flag
805 {3, 8, N, 2, 6, 2 }, // modulator level scaling (This is actually reversed bits...)
806 {3, 9, N, 0, 0, 4 }, // modulator frequency multiplier
807 {3, 10, N, 8, 0, 3 }, // modulator wave select
808 {3, 11, B, 0, 6, 1 }, // modulator pitch vibrato
809 {3, 12, B, 0, 7, 1 }, // modulator volume vibrato
810 };
811
do_adlibconfig(UNUSED void * data)812 static void do_adlibconfig(UNUSED void *data)
813 {
814 //page->help_index = HELP_SAMPLE_LIST;
815
816 song_sample_t *sample = song_get_sample(current_sample);
817 if (sample->data)
818 csf_free_sample(sample->data);
819 sample->data = csf_allocate_sample(1);
820 sample->length = 1;
821 if (!(sample->flags & CHN_ADLIB)) {
822 sample->flags |= CHN_ADLIB;
823 status_text_flash("Created adlib sample");
824 }
825 sample->flags &= ~(CHN_16BIT | CHN_STEREO
826 | CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN);
827 sample->loop_start = sample->loop_end = 0;
828 sample->sustain_start = sample->sustain_end = 0;
829 if (!sample->c5speed) {
830 sample->c5speed = 8363;
831 sample->volume = 64 * 4;
832 sample->global_volume = 64;
833 }
834 sample_host_dialog(-1);
835
836 status.flags |= SONG_NEEDS_SAVE;
837 }
838
adlibconfig_refresh(void)839 static void adlibconfig_refresh(void)
840 {
841 int a;
842 song_sample_t *sample = song_get_sample(current_sample);
843
844 draw_sample_data(&sample_image, sample);
845
846 for (a = 0; a < ARRAY_SIZE(adlibconfig_widgets); a++) {
847 unsigned int srcvalue = 0;
848 unsigned int maskvalue = 0xFFFF;
849 unsigned int nbits_real = (adlibconfig_widgets[a].nbits < 0
850 ? -adlibconfig_widgets[a].nbits
851 : adlibconfig_widgets[a].nbits);
852 unsigned int maxvalue = (1 << nbits_real) - 1;
853
854 switch (adlibconfig_widgets[a].type) {
855 case B: srcvalue = sample_adlibconfig_widgets[a].d.toggle.state; break;
856 case N: srcvalue = sample_adlibconfig_widgets[a].d.numentry.value; break;
857 }
858
859 if(adlibconfig_widgets[a].nbits < 0)
860 srcvalue = maxvalue - srcvalue; // reverse the semantics
861
862 srcvalue &= maxvalue; srcvalue <<= adlibconfig_widgets[a].firstbit;
863 maskvalue &= maxvalue; maskvalue <<= adlibconfig_widgets[a].firstbit;
864
865 sample->adlib_bytes[adlibconfig_widgets[a].byteno] =
866 (sample->adlib_bytes[adlibconfig_widgets[a].byteno] &~ maskvalue) | srcvalue;
867 }
868 }
869
sample_adlibconfig_draw_const(void)870 static void sample_adlibconfig_draw_const(void)
871 {
872 struct {
873 int x, y;
874 const char *label;
875 } labels[] = {
876 {19, 1, "Adlib Melodic Instrument Parameters"},
877 {19, 3, "Additive Synthesis:"},
878 {18, 4, "Modulation Feedback:"},
879 {26, 6, "Car Mod"},
880 {19, 7, "Attack"},
881 {20, 8, "Decay"},
882 {18, 9, "Sustain"},
883 {18, 10, "Release"},
884 {12, 11, "Sustain Sound"},
885 {19, 12, "Volume"},
886 {58, 6, "Car Mod"},
887 {43, 7, "Scale Envelope"},
888 {44, 8, "Level Scaling"},
889 {37, 9, "Frequency Multiplier"},
890 {46, 10, "Wave Select"},
891 {44, 11, "Pitch Vibrato"},
892 {43, 12, "Volume Vibrato"},
893 };
894
895 int a;
896
897 // 39 33
898 draw_box(38, 2 + 30, 40, 5 + 30, BOX_THIN | BOX_INNER | BOX_INSET);
899
900 draw_fill_chars(25, 6 + 30, 32,13 + 30, 0);
901 draw_box(25, 6 + 30, 28, 13 + 30, BOX_THIN | BOX_INNER | BOX_INSET);
902 draw_box(29, 6 + 30, 32, 13 + 30, BOX_THIN | BOX_INNER | BOX_INSET);
903
904 draw_fill_chars(57, 6 + 30, 64,13 + 30, 0);
905 draw_box(57, 6 + 30, 60, 13 + 30, BOX_THIN | BOX_INNER | BOX_INSET);
906 draw_box(61, 6 + 30, 64, 13 + 30, BOX_THIN | BOX_INNER | BOX_INSET);
907
908 for (a = 0; a < ARRAY_SIZE(labels); a++)
909 draw_text(labels[a].label, labels[a].x, labels[a].y + 30, a ? 0 : 3, 2);
910 }
911
do_adlib_handlekey(struct key_event * kk)912 static int do_adlib_handlekey(struct key_event *kk)
913 {
914 if (kk->sym == SDLK_F1) {
915 if (kk->state == KEY_PRESS)
916 return 1;
917 status.current_help_index = HELP_ADLIB_SAMPLE;
918 dialog_f1_hack = 1;
919 dialog_destroy_all();
920 set_page(PAGE_HELP);
921 return 1;
922 }
923 return 0;
924 }
925
sample_adlibconfig_dialog(UNUSED void * ign)926 static void sample_adlibconfig_dialog(UNUSED void *ign)
927 {
928 struct dialog *dialog;
929 song_sample_t *sample = song_get_sample(current_sample);
930
931 int a;
932
933 //page->help_index = HELP_ADLIB_SAMPLES;
934 // Eh, what page? Where am I supposed to get a reference to page?
935 // How do I make this work? -Bisqwit
936
937 for (a = 0; a < ARRAY_SIZE(adlibconfig_widgets); a++) {
938 unsigned int srcvalue = sample->adlib_bytes[adlibconfig_widgets[a].byteno];
939 unsigned int nbits_real = adlibconfig_widgets[a].nbits < 0
940 ? -adlibconfig_widgets[a].nbits
941 : adlibconfig_widgets[a].nbits;
942 unsigned int minvalue = 0, maxvalue = (1 << nbits_real) - 1;
943
944 srcvalue >>= adlibconfig_widgets[a].firstbit;
945 srcvalue &= maxvalue;
946 if (adlibconfig_widgets[a].nbits < 0)
947 srcvalue = maxvalue - srcvalue; // reverse the semantics
948
949 switch (adlibconfig_widgets[a].type) {
950 case B:
951 create_menutoggle(sample_adlibconfig_widgets + a,
952 adlib_xpos[adlibconfig_widgets[a].xref],
953 adlibconfig_widgets[a].y + 30,
954 a > 0 ? a - 1 : 0,
955 a + 1 < ARRAY_SIZE(adlibconfig_widgets) ? a + 1 : a,
956 a, a,
957 (a > 1 ? ((a + 4) % (ARRAY_SIZE(adlibconfig_widgets) - 2)) + 2 : 2),
958 adlibconfig_refresh, yn_toggle);
959 sample_adlibconfig_widgets[a].d.menutoggle.state = srcvalue;
960 sample_adlibconfig_widgets[a].d.menutoggle.activation_keys = "ny";
961 break;
962 case N:
963 create_numentry(sample_adlibconfig_widgets + a,
964 adlib_xpos[adlibconfig_widgets[a].xref],
965 adlibconfig_widgets[a].y + 30,
966 nbits_real < 4 ? 1 : 2,
967 a > 0 ? a - 1 : 0,
968 a + 1 < ARRAY_SIZE(adlibconfig_widgets) ? a + 1 : a,
969 (a > 1 ? ((a + 4) % (ARRAY_SIZE(adlibconfig_widgets) - 2)) + 2 : 2),
970 adlibconfig_refresh,
971 minvalue, maxvalue,
972 adlib_cursorpos + adlibconfig_widgets[a].xref);
973 sample_adlibconfig_widgets[a].d.numentry.value = srcvalue;
974 break;
975 }
976 }
977
978 dialog = dialog_create_custom(9, 30, 61, 15, sample_adlibconfig_widgets,
979 ARRAY_SIZE(adlibconfig_widgets), 0,
980 sample_adlibconfig_draw_const, NULL);
981 dialog->action_yes = do_adlibconfig;
982 dialog->handle_key = do_adlib_handlekey;
983 }
984
985
sample_adlibpatch_finish(int n)986 static void sample_adlibpatch_finish(int n)
987 {
988 song_sample_t *sample;
989
990 if (n <= 0 || n > 128)
991 return;
992
993 sample = song_get_sample(current_sample);
994 adlib_patch_apply((song_sample_t *) sample, n - 1);
995 status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE; // redraw the sample
996
997 sample_host_dialog(-1);
998 }
999
sample_adlibpatch_dialog(UNUSED void * ign)1000 static void sample_adlibpatch_dialog(UNUSED void *ign)
1001 {
1002 numprompt_create("Enter Patch (1-128)", sample_adlibpatch_finish, 0);
1003 }
1004
1005 /* --------------------------------------------------------------------- */
1006
1007 /* filename can be NULL, in which case the sample filename is used (quick save) */
1008 struct sample_save_data {
1009 char *path;
1010 /* char *options? */
1011 const char *format;
1012 };
1013
save_sample_free_data(void * ptr)1014 static void save_sample_free_data(void *ptr)
1015 {
1016 struct sample_save_data *data = (struct sample_save_data *) ptr;
1017 if (data->path)
1018 free(data->path);
1019 free(data);
1020 }
1021
do_save_sample(void * ptr)1022 static void do_save_sample(void *ptr)
1023 {
1024 struct sample_save_data *data = (struct sample_save_data *) ptr;
1025
1026 // I guess this function doesn't need to care about the return value,
1027 // since song_save_sample is handling all the visual feedback...
1028 song_save_sample(data->path, data->format, song_get_sample(current_sample), current_sample);
1029 save_sample_free_data(ptr);
1030 }
1031
sample_save(const char * filename,const char * format)1032 static void sample_save(const char *filename, const char *format)
1033 {
1034 song_sample_t *sample = song_get_sample(current_sample);
1035 char *ptr, *q;
1036 struct sample_save_data *data;
1037 struct stat buf;
1038 int tmp;
1039
1040 if (stat(cfg_dir_samples, &buf) == -1) {
1041 status_text_flash("Sample directory \"%s\" unreachable", filename);
1042 return;
1043 }
1044
1045 tmp=0;
1046 data = mem_alloc(sizeof(struct sample_save_data));
1047 if (!S_ISDIR(buf.st_mode)) {
1048 /* directory browsing */
1049 q = strrchr(cfg_dir_samples, DIR_SEPARATOR);
1050 if (q) {
1051 tmp = q[1];
1052 q[1] = '\0';
1053 }
1054 } else {
1055 q = NULL;
1056 }
1057
1058 ptr = dmoz_path_concat(cfg_dir_samples, filename ? : sample->filename);
1059 if (q) q[1] = tmp;
1060
1061 data->path = ptr;
1062 data->format = format;
1063
1064 if (filename && *filename && stat(ptr, &buf) == 0) {
1065 if (S_ISREG(buf.st_mode)) {
1066 dialog_create(DIALOG_OK_CANCEL, "Overwrite file?",
1067 do_save_sample, save_sample_free_data, 1, data);
1068 /* callback will free it */
1069 } else if (S_ISDIR(buf.st_mode)) {
1070 status_text_flash("%s is a directory", filename);
1071 save_sample_free_data(data);
1072 } else {
1073 status_text_flash("%s is not a regular file", filename);
1074 save_sample_free_data(data);
1075 }
1076 } else {
1077 do_save_sample(data);
1078 }
1079 }
1080
1081 /* export sample dialog */
1082
1083 static struct widget export_sample_widgets[4];
1084 static char export_sample_filename[NAME_MAX + 1] = "";
1085 static int export_sample_format = 0;
1086
do_export_sample(UNUSED void * data)1087 static void do_export_sample(UNUSED void *data)
1088 {
1089 sample_save(export_sample_filename, sample_save_formats[export_sample_format].label);
1090 }
1091
export_sample_list_draw(void)1092 static void export_sample_list_draw(void)
1093 {
1094 int n, focused = (*selected_widget == 3);
1095
1096 draw_fill_chars(53, 24, 56, 31, 0);
1097 for (n = 0; sample_save_formats[n].label; n++) {
1098 int fg = 6, bg = 0;
1099 if (focused && n == export_sample_format) {
1100 fg = 0;
1101 bg = 3;
1102 } else if (n == export_sample_format) {
1103 bg = 14;
1104 }
1105 draw_text_len(sample_save_formats[n].label, 4, 53, 24 + n, fg, bg);
1106 }
1107 }
1108
export_sample_list_handle_key(struct key_event * k)1109 static int export_sample_list_handle_key(struct key_event * k)
1110 {
1111 int new_format = export_sample_format;
1112
1113 if (k->state == KEY_RELEASE)
1114 return 0;
1115 switch (k->sym) {
1116 case SDLK_UP:
1117 if (!NO_MODIFIER(k->mod))
1118 return 0;
1119 new_format--;
1120 break;
1121 case SDLK_DOWN:
1122 if (!NO_MODIFIER(k->mod))
1123 return 0;
1124 new_format++;
1125 break;
1126 case SDLK_PAGEUP:
1127 case SDLK_HOME:
1128 if (!NO_MODIFIER(k->mod))
1129 return 0;
1130 new_format = 0;
1131 break;
1132 case SDLK_PAGEDOWN:
1133 case SDLK_END:
1134 if (!NO_MODIFIER(k->mod))
1135 return 0;
1136 new_format = num_save_formats - 1;
1137 break;
1138 case SDLK_TAB:
1139 if (k->mod & KMOD_SHIFT) {
1140 change_focus_to(0);
1141 return 1;
1142 }
1143 /* fall through */
1144 case SDLK_LEFT:
1145 case SDLK_RIGHT:
1146 if (!NO_MODIFIER(k->mod))
1147 return 0;
1148 change_focus_to(0); /* should focus 0/1/2 depending on what's closest */
1149 return 1;
1150 default:
1151 return 0;
1152 }
1153
1154 new_format = CLAMP(new_format, 0, num_save_formats - 1);
1155 if (new_format != export_sample_format) {
1156 /* update the option string */
1157 export_sample_format = new_format;
1158 status.flags |= NEED_UPDATE;
1159 }
1160
1161 return 1;
1162 }
1163
export_sample_draw_const(void)1164 static void export_sample_draw_const(void)
1165 {
1166 draw_text("Export Sample", 34, 21, 0, 2);
1167
1168 draw_text("Filename", 24, 24, 0, 2);
1169 draw_box(32, 23, 51, 25, BOX_THICK | BOX_INNER | BOX_INSET);
1170
1171 draw_box(52, 23, 57, 32, BOX_THICK | BOX_INNER | BOX_INSET);
1172 }
1173
export_sample_dialog(void)1174 static void export_sample_dialog(void)
1175 {
1176 song_sample_t *sample = song_get_sample(current_sample);
1177 struct dialog *dialog;
1178
1179 create_textentry(export_sample_widgets + 0, 33, 24, 18, 0, 1, 3, NULL,
1180 export_sample_filename, NAME_MAX);
1181 create_button(export_sample_widgets + 1, 31, 35, 6, 0, 1, 2, 2, 2, dialog_yes_NULL, "OK", 3);
1182 create_button(export_sample_widgets + 2, 42, 35, 6, 3, 2, 1, 1, 1, dialog_cancel_NULL, "Cancel", 1);
1183 create_other(export_sample_widgets + 3, 0, export_sample_list_handle_key, export_sample_list_draw);
1184
1185 strncpy(export_sample_filename, sample->filename, NAME_MAX);
1186 export_sample_filename[NAME_MAX] = 0;
1187
1188 dialog = dialog_create_custom(21, 20, 39, 18, export_sample_widgets, 4, 0,
1189 export_sample_draw_const, NULL);
1190 dialog->action_yes = do_export_sample;
1191 }
1192
1193
1194 /* resize sample dialog */
1195 static struct widget resize_sample_widgets[2];
1196 static int resize_sample_cursor;
1197
do_resize_sample_aa(UNUSED void * data)1198 static void do_resize_sample_aa(UNUSED void *data)
1199 {
1200 song_sample_t *sample = song_get_sample(current_sample);
1201 unsigned int newlen = resize_sample_widgets[0].d.numentry.value;
1202 sample_resize(sample, newlen, 1);
1203 }
1204
do_resize_sample(UNUSED void * data)1205 static void do_resize_sample(UNUSED void *data)
1206 {
1207 song_sample_t *sample = song_get_sample(current_sample);
1208 unsigned int newlen = resize_sample_widgets[0].d.numentry.value;
1209 sample_resize(sample, newlen, 0);
1210 }
1211
resize_sample_draw_const(void)1212 static void resize_sample_draw_const(void)
1213 {
1214 draw_text("Resize Sample", 34, 24, 3, 2);
1215 draw_text("New Length", 31, 27, 0, 2);
1216 draw_box(41, 26, 49, 28, BOX_THICK | BOX_INNER | BOX_INSET);
1217 }
1218
resize_sample_dialog(int aa)1219 static void resize_sample_dialog(int aa)
1220 {
1221 song_sample_t *sample = song_get_sample(current_sample);
1222 struct dialog *dialog;
1223
1224 resize_sample_cursor = 0;
1225 create_numentry(resize_sample_widgets + 0, 42, 27, 7, 0, 1, 1, NULL, 0, 9999999, &resize_sample_cursor);
1226 resize_sample_widgets[0].d.numentry.value = sample->length;
1227 create_button(resize_sample_widgets + 1, 36, 30, 6, 0, 1, 1, 1, 1,
1228 dialog_cancel_NULL, "Cancel", 1);
1229 dialog = dialog_create_custom(26, 22, 29, 11, resize_sample_widgets, 2, 0,
1230 resize_sample_draw_const, NULL);
1231 dialog->action_yes = aa ? do_resize_sample_aa : do_resize_sample;
1232 }
1233
1234 /* --------------------------------------------------------------------- */
1235
sample_set_mute(int n,int mute)1236 static void sample_set_mute(int n, int mute)
1237 {
1238 song_sample_t *smp = song_get_sample(n);
1239
1240 if (mute) {
1241 if (smp->flags & CHN_MUTE)
1242 return;
1243 smp->globalvol_saved = smp->global_volume;
1244 smp->global_volume = 0;
1245 smp->flags |= CHN_MUTE;
1246 } else {
1247 if (!(smp->flags & CHN_MUTE))
1248 return;
1249 smp->global_volume = smp->globalvol_saved;
1250 smp->flags &= ~CHN_MUTE;
1251 }
1252 }
1253
sample_toggle_mute(int n)1254 static void sample_toggle_mute(int n)
1255 {
1256 song_sample_t *smp = song_get_sample(n);
1257 sample_set_mute(n, !(smp->flags & CHN_MUTE));
1258 }
1259
sample_toggle_solo(int n)1260 static void sample_toggle_solo(int n)
1261 {
1262 int i, solo = 0;
1263
1264 if (song_get_sample(n)->flags & CHN_MUTE) {
1265 solo = 1;
1266 } else {
1267 for (i = 1; i < MAX_SAMPLES; i++) {
1268 if (i != n && !(song_get_sample(i)->flags & CHN_MUTE)) {
1269 solo = 1;
1270 break;
1271 }
1272 }
1273 }
1274 for (i = 1; i < MAX_SAMPLES; i++)
1275 sample_set_mute(i, solo && i != n);
1276 }
1277
1278 /* --------------------------------------------------------------------- */
1279
sample_list_handle_alt_key(struct key_event * k)1280 static void sample_list_handle_alt_key(struct key_event * k)
1281 {
1282 song_sample_t *sample = song_get_sample(current_sample);
1283 int canmod = (sample->data != NULL && !(sample->flags & CHN_ADLIB));
1284
1285 if (k->state == KEY_RELEASE)
1286 return;
1287 switch (k->sym) {
1288 case SDLK_a:
1289 if (canmod)
1290 dialog_create(DIALOG_OK_CANCEL, "Convert sample?", do_sign_convert, NULL, 0, NULL);
1291 return;
1292 case SDLK_b:
1293 if (canmod && (sample->loop_start > 0
1294 || ((sample->flags & CHN_SUSTAINLOOP) && sample->sustain_start > 0))) {
1295 dialog_create(DIALOG_OK_CANCEL, "Cut sample?", do_pre_loop_cut, NULL, 1, NULL);
1296 }
1297 return;
1298 case SDLK_d:
1299 if ((k->mod & KMOD_SHIFT) && !(status.flags & CLASSIC_MODE)) {
1300 if (canmod && sample->flags & CHN_STEREO) {
1301 dialog_create(DIALOG_OK_CANCEL, "Downmix sample to mono?",
1302 do_downmix, NULL, 0, NULL);
1303 }
1304 } else {
1305 dialog_create(DIALOG_OK_CANCEL, "Delete sample?", do_delete_sample,
1306 NULL, 1, NULL);
1307 }
1308 return;
1309 case SDLK_e:
1310 if (canmod)
1311 resize_sample_dialog(1);
1312 break;
1313 case SDLK_f:
1314 if (canmod)
1315 resize_sample_dialog(0);
1316 break;
1317 case SDLK_g:
1318 if (canmod)
1319 sample_reverse(sample);
1320 break;
1321 case SDLK_h:
1322 if (canmod)
1323 dialog_create(DIALOG_YES_NO, "Centralise sample?", do_centralise, NULL, 0, NULL);
1324 return;
1325 case SDLK_i:
1326 if (canmod)
1327 sample_invert(sample);
1328 break;
1329 case SDLK_l:
1330 if (canmod && (sample->loop_end > 0
1331 || ((sample->flags & CHN_SUSTAINLOOP) && sample->sustain_end > 0))) {
1332 dialog_create(DIALOG_OK_CANCEL, "Cut sample?", do_post_loop_cut, NULL, 1, NULL);
1333 }
1334 return;
1335 case SDLK_m:
1336 if (canmod)
1337 sample_amplify_dialog();
1338 return;
1339 case SDLK_n:
1340 song_toggle_multichannel_mode();
1341 return;
1342 case SDLK_o:
1343 sample_save(NULL, "ITS");
1344 return;
1345 case SDLK_p:
1346 smpprompt_create("Copy sample:", "Sample", do_copy_sample);
1347 return;
1348 case SDLK_q:
1349 if (canmod) {
1350 dialog_create(DIALOG_YES_NO, "Convert sample?",
1351 do_quality_convert, do_quality_toggle, 0, NULL);
1352 }
1353 return;
1354 case SDLK_r:
1355 smpprompt_create("Replace sample with:", "Sample", do_replace_sample);
1356 return;
1357 case SDLK_s:
1358 smpprompt_create("Swap sample with:", "Sample", do_swap_sample);
1359 return;
1360 case SDLK_t:
1361 export_sample_dialog();
1362 return;
1363 case SDLK_w:
1364 sample_save(NULL, "RAW");
1365 return;
1366 case SDLK_x:
1367 smpprompt_create("Exchange sample with:", "Sample", do_exchange_sample);
1368 return;
1369 case SDLK_y:
1370 /* hi virt */
1371 txtsynth_dialog();
1372 return;
1373 case SDLK_z:
1374 { // uguu~
1375 void (*dlg)(void *) = (k->mod & KMOD_SHIFT)
1376 ? sample_adlibpatch_dialog
1377 : sample_adlibconfig_dialog;
1378 if (canmod) {
1379 dialog_create(DIALOG_OK_CANCEL, "This will replace the current sample.",
1380 dlg, NULL, 1, NULL);
1381 } else {
1382 dlg(NULL);
1383 }
1384 }
1385 return;
1386 case SDLK_INSERT:
1387 song_insert_sample_slot(current_sample);
1388 break;
1389 case SDLK_DELETE:
1390 song_remove_sample_slot(current_sample);
1391 break;
1392 case SDLK_F9:
1393 sample_toggle_mute(current_sample);
1394 break;
1395 case SDLK_F10:
1396 sample_toggle_solo(current_sample);
1397 break;
1398 default:
1399 return;
1400 }
1401
1402 status.flags |= NEED_UPDATE;
1403 }
1404
sample_list_handle_key(struct key_event * k)1405 static void sample_list_handle_key(struct key_event * k)
1406 {
1407 int new_sample = current_sample;
1408 song_sample_t *sample = song_get_sample(current_sample);
1409
1410 switch (k->sym) {
1411 case SDLK_SPACE:
1412 if (k->state == KEY_RELEASE)
1413 return;
1414 if (selected_widget && *selected_widget == 0) {
1415 status.flags |= NEED_UPDATE;
1416 }
1417 return;
1418 case SDLK_PLUS:
1419 if (k->state == KEY_RELEASE)
1420 return;
1421 if (k->mod & KMOD_ALT) {
1422 sample->c5speed *= 2;
1423 status.flags |= SONG_NEEDS_SAVE;
1424 } else if (k->mod & KMOD_CTRL) {
1425 sample->c5speed = calc_halftone(sample->c5speed, 1);
1426 status.flags |= SONG_NEEDS_SAVE;
1427 }
1428 status.flags |= NEED_UPDATE;
1429 return;
1430 case SDLK_MINUS:
1431 if (k->state == KEY_RELEASE)
1432 return;
1433 if (k->mod & KMOD_ALT) {
1434 sample->c5speed /= 2;
1435 status.flags |= SONG_NEEDS_SAVE;
1436 } else if (k->mod & KMOD_CTRL) {
1437 sample->c5speed = calc_halftone(sample->c5speed, -1);
1438 status.flags |= SONG_NEEDS_SAVE;
1439 }
1440 status.flags |= NEED_UPDATE;
1441 return;
1442
1443 case SDLK_COMMA:
1444 case SDLK_LESS:
1445 if (k->state == KEY_RELEASE)
1446 return;
1447 song_change_current_play_channel(-1, 0);
1448 return;
1449 case SDLK_PERIOD:
1450 case SDLK_GREATER:
1451 if (k->state == KEY_RELEASE)
1452 return;
1453 song_change_current_play_channel(1, 0);
1454 return;
1455 case SDLK_PAGEUP:
1456 if (k->state == KEY_RELEASE)
1457 return;
1458 new_sample--;
1459 break;
1460 case SDLK_PAGEDOWN:
1461 if (k->state == KEY_RELEASE)
1462 return;
1463 new_sample++;
1464 break;
1465 case SDLK_ESCAPE:
1466 if (k->mod & KMOD_SHIFT) {
1467 if (k->state == KEY_RELEASE)
1468 return;
1469 sample_list_cursor_pos = 25;
1470 _fix_accept_text();
1471 change_focus_to(0);
1472 status.flags |= NEED_UPDATE;
1473 return;
1474 }
1475 return;
1476 default:
1477 if (k->mod & KMOD_ALT) {
1478 if (k->state == KEY_RELEASE)
1479 return;
1480 sample_list_handle_alt_key(k);
1481 } else if (!k->is_repeat) {
1482 int n, v;
1483 if (k->midi_note > -1) {
1484 n = k->midi_note;
1485 if (k->midi_volume > -1) {
1486 v = k->midi_volume / 2;
1487 } else {
1488 v = KEYJAZZ_DEFAULTVOL;
1489 }
1490 } else {
1491 n = (k->sym == SDLK_SPACE)
1492 ? last_note
1493 : kbd_get_note(k);
1494 if (n <= 0 || n > 120)
1495 return;
1496 v = KEYJAZZ_DEFAULTVOL;
1497 }
1498 if (k->state == KEY_RELEASE) {
1499 song_keyup(current_sample, KEYJAZZ_NOINST, n);
1500 } else {
1501 song_keydown(current_sample, KEYJAZZ_NOINST, n, v, KEYJAZZ_CHAN_CURRENT);
1502 last_note = n;
1503 }
1504 }
1505 return;
1506 }
1507
1508 new_sample = CLAMP(new_sample, 1, _last_vis_sample());
1509
1510 if (new_sample != current_sample) {
1511 sample_set(new_sample);
1512 sample_list_reposition();
1513 status.flags |= NEED_UPDATE;
1514 }
1515 }
1516
1517 /* --------------------------------------------------------------------- */
1518 /* wheesh */
1519
sample_list_draw_const(void)1520 static void sample_list_draw_const(void)
1521 {
1522 int n;
1523
1524 draw_box(4, 12, 35, 48, BOX_THICK | BOX_INNER | BOX_INSET);
1525 draw_box(63, 12, 77, 24, BOX_THICK | BOX_INNER | BOX_INSET);
1526
1527 draw_box(36, 12, 53, 18, BOX_THIN | BOX_INNER | BOX_INSET);
1528 draw_box(37, 15, 47, 17, BOX_THIN | BOX_INNER | BOX_INSET);
1529 draw_box(36, 19, 53, 25, BOX_THIN | BOX_INNER | BOX_INSET);
1530 draw_box(37, 22, 47, 24, BOX_THIN | BOX_INNER | BOX_INSET);
1531 draw_box(36, 26, 53, 33, BOX_THIN | BOX_INNER | BOX_INSET);
1532 draw_box(37, 29, 47, 32, BOX_THIN | BOX_INNER | BOX_INSET);
1533 draw_box(36, 35, 53, 41, BOX_THIN | BOX_INNER | BOX_INSET);
1534 draw_box(37, 38, 47, 40, BOX_THIN | BOX_INNER | BOX_INSET);
1535 draw_box(36, 42, 53, 48, BOX_THIN | BOX_INNER | BOX_INSET);
1536 draw_box(37, 45, 47, 47, BOX_THIN | BOX_INNER | BOX_INSET);
1537 draw_box(54, 25, 77, 30, BOX_THIN | BOX_INNER | BOX_INSET);
1538 draw_box(54, 31, 77, 41, BOX_THIN | BOX_INNER | BOX_INSET);
1539 draw_box(54, 42, 77, 48, BOX_THIN | BOX_INNER | BOX_INSET);
1540 draw_box(55, 45, 72, 47, BOX_THIN | BOX_INNER | BOX_INSET);
1541
1542 draw_fill_chars(41, 30, 46, 30, 0);
1543 draw_fill_chars(64, 13, 76, 23, 0);
1544
1545 draw_text("Default Volume", 38, 14, 0, 2);
1546 draw_text("Global Volume", 38, 21, 0, 2);
1547 draw_text("Default Pan", 39, 28, 0, 2);
1548 draw_text("Vibrato Speed", 38, 37, 0, 2);
1549 draw_text("Vibrato Depth", 38, 44, 0, 2);
1550 draw_text("Filename", 55, 13, 0, 2);
1551 draw_text("Speed", 58, 14, 0, 2);
1552 draw_text("Loop", 59, 15, 0, 2);
1553 draw_text("LoopBeg", 56, 16, 0, 2);
1554 draw_text("LoopEnd", 56, 17, 0, 2);
1555 draw_text("SusLoop", 56, 18, 0, 2);
1556 draw_text("SusLBeg", 56, 19, 0, 2);
1557 draw_text("SusLEnd", 56, 20, 0, 2);
1558 draw_text("Quality", 56, 22, 0, 2);
1559 draw_text("Length", 57, 23, 0, 2);
1560 draw_text("Vibrato Waveform", 58, 33, 0, 2);
1561 draw_text("Vibrato Rate", 60, 44, 0, 2);
1562
1563 for (n = 0; n < 13; n++)
1564 draw_char(154, 64 + n, 21, 3, 0);
1565 }
1566
1567 /* --------------------------------------------------------------------- */
1568 /* wow. this got ugly. */
1569
1570 /* callback for the loop menu toggles */
update_sample_loop_flags(void)1571 static void update_sample_loop_flags(void)
1572 {
1573 song_sample_t *sample = song_get_sample(current_sample);
1574
1575 /* these switch statements fall through */
1576 sample->flags &= ~(CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN);
1577 switch (widgets_samplelist[9].d.menutoggle.state) {
1578 case 2: sample->flags |= CHN_PINGPONGLOOP;
1579 case 1: sample->flags |= CHN_LOOP;
1580 }
1581
1582 switch (widgets_samplelist[12].d.menutoggle.state) {
1583 case 2: sample->flags |= CHN_PINGPONGSUSTAIN;
1584 case 1: sample->flags |= CHN_SUSTAINLOOP;
1585 }
1586
1587 if (sample->flags & CHN_LOOP) {
1588 if (sample->loop_start == sample->length)
1589 sample->loop_start = 0;
1590 if (sample->loop_end <= sample->loop_start)
1591 sample->loop_end = sample->length;
1592 }
1593
1594 if (sample->flags & CHN_SUSTAINLOOP) {
1595 if (sample->sustain_start == sample->length)
1596 sample->sustain_start = 0;
1597 if (sample->sustain_end <= sample->sustain_start)
1598 sample->sustain_end = sample->length;
1599 }
1600
1601 csf_adjust_sample_loop(sample);
1602
1603 /* update any samples currently playing */
1604 song_update_playing_sample(current_sample);
1605
1606 status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
1607 }
1608
1609 /* callback for the loop numentries */
update_sample_loop_points(void)1610 static void update_sample_loop_points(void)
1611 {
1612 song_sample_t *sample = song_get_sample(current_sample);
1613 int flags_changed = 0;
1614
1615 /* 9 = loop toggle, 10 = loop start, 11 = loop end */
1616 if ((unsigned long) widgets_samplelist[10].d.numentry.value > sample->length - 1)
1617 widgets_samplelist[10].d.numentry.value = sample->length - 1;
1618 if (widgets_samplelist[11].d.numentry.value <= widgets_samplelist[10].d.numentry.value) {
1619 widgets_samplelist[9].d.menutoggle.state = 0;
1620 flags_changed = 1;
1621 } else if ((unsigned long) widgets_samplelist[11].d.numentry.value > sample->length) {
1622 widgets_samplelist[11].d.numentry.value = sample->length;
1623 }
1624 if (sample->loop_start != (unsigned long) widgets_samplelist[10].d.numentry.value
1625 || sample->loop_end != (unsigned long) widgets_samplelist[11].d.numentry.value) {
1626 flags_changed = 1;
1627 }
1628 sample->loop_start = widgets_samplelist[10].d.numentry.value;
1629 sample->loop_end = widgets_samplelist[11].d.numentry.value;
1630
1631 /* 12 = sus toggle, 13 = sus start, 14 = sus end */
1632 if ((unsigned long) widgets_samplelist[13].d.numentry.value > sample->length - 1)
1633 widgets_samplelist[13].d.numentry.value = sample->length - 1;
1634 if (widgets_samplelist[14].d.numentry.value <= widgets_samplelist[13].d.numentry.value) {
1635 widgets_samplelist[12].d.menutoggle.state = 0;
1636 flags_changed = 1;
1637 } else if ((unsigned long) widgets_samplelist[14].d.numentry.value > sample->length) {
1638 widgets_samplelist[14].d.numentry.value = sample->length;
1639 }
1640 if (sample->sustain_start != (unsigned long) widgets_samplelist[13].d.numentry.value
1641 || sample->sustain_end != (unsigned long) widgets_samplelist[14].d.numentry.value) {
1642 flags_changed = 1;
1643 }
1644 sample->sustain_start = widgets_samplelist[13].d.numentry.value;
1645 sample->sustain_end = widgets_samplelist[14].d.numentry.value;
1646
1647 if (flags_changed) {
1648 update_sample_loop_flags();
1649 }
1650
1651 csf_adjust_sample_loop(sample);
1652
1653 status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
1654 }
1655
1656 /* --------------------------------------------------------------------- */
1657
update_values_in_song(void)1658 static void update_values_in_song(void)
1659 {
1660 song_sample_t *sample = song_get_sample(current_sample);
1661
1662 /* a few more modplug hacks here... */
1663 sample->volume = widgets_samplelist[1].d.thumbbar.value * 4;
1664 sample->global_volume = widgets_samplelist[2].d.thumbbar.value;
1665
1666 if (widgets_samplelist[3].d.toggle.state)
1667 sample->flags |= CHN_PANNING;
1668 else
1669 sample->flags &= ~CHN_PANNING;
1670 sample->vib_speed = widgets_samplelist[5].d.thumbbar.value;
1671 sample->vib_depth = widgets_samplelist[6].d.thumbbar.value;
1672
1673 if (widgets_samplelist[15].d.togglebutton.state)
1674 sample->vib_type = VIB_SINE;
1675 else if (widgets_samplelist[16].d.togglebutton.state)
1676 sample->vib_type = VIB_RAMP_DOWN;
1677 else if (widgets_samplelist[17].d.togglebutton.state)
1678 sample->vib_type = VIB_SQUARE;
1679 else
1680 sample->vib_type = VIB_RANDOM;
1681 sample->vib_rate = widgets_samplelist[19].d.thumbbar.value;
1682
1683 status.flags |= SONG_NEEDS_SAVE;
1684 }
1685
update_sample_speed(void)1686 static void update_sample_speed(void)
1687 {
1688 song_sample_t *sample = song_get_sample(current_sample);
1689
1690 sample->c5speed = widgets_samplelist[8].d.numentry.value;
1691
1692 status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
1693 }
1694
update_panning(void)1695 static void update_panning(void)
1696 {
1697 song_sample_t *sample = song_get_sample(current_sample);
1698
1699 sample->flags |= CHN_PANNING;
1700 sample->panning = widgets_samplelist[4].d.thumbbar.value * 4;
1701
1702 widgets_samplelist[3].d.toggle.state = 1;
1703
1704 status.flags |= SONG_NEEDS_SAVE;
1705 }
1706
update_filename(void)1707 static void update_filename(void)
1708 {
1709 status.flags |= SONG_NEEDS_SAVE;
1710 }
1711
1712 /* --------------------------------------------------------------------- */
1713
sample_is_used_by_instrument(int samp)1714 int sample_is_used_by_instrument(int samp)
1715 {
1716 song_instrument_t *ins;
1717 int i, j;
1718 if (samp < 1) return 0;
1719 for (i = 1; i <= MAX_INSTRUMENTS; i++) {
1720 ins = song_get_instrument(i);
1721 if (!ins) continue;
1722 for (j = 0; j < 120; j++) {
1723 if (ins->sample_map[j] == (unsigned int)samp)
1724 return 1;
1725 }
1726 }
1727 return 0;
1728 }
1729
sample_synchronize_to_instrument(void)1730 void sample_synchronize_to_instrument(void)
1731 {
1732 song_instrument_t *ins;
1733 int instnum = instrument_get_current();
1734 int pos, first;
1735
1736 ins = song_get_instrument(instnum);
1737 first = 0;
1738 for (pos = 0; pos < 120; pos++) {
1739 if (first == 0) first = ins->sample_map[pos];
1740 if (ins->sample_map[pos] == (unsigned int)instnum) {
1741 sample_set(instnum);
1742 return;
1743 }
1744 }
1745 if (first > 0) {
1746 sample_set(first);
1747 } else {
1748 sample_set(instnum);
1749 }
1750 }
1751
sample_list_load_page(struct page * page)1752 void sample_list_load_page(struct page *page)
1753 {
1754 vgamem_ovl_alloc(&sample_image);
1755
1756 page->title = "Sample List (F3)";
1757 page->draw_const = sample_list_draw_const;
1758 page->predraw_hook = sample_list_predraw_hook;
1759 page->handle_key = sample_list_handle_key;
1760 page->set_page = sample_list_reposition;
1761 page->total_widgets = 20;
1762 page->widgets = widgets_samplelist;
1763 page->help_index = HELP_SAMPLE_LIST;
1764
1765 /* 0 = sample list */
1766 create_other(widgets_samplelist + 0, 1, sample_list_handle_key_on_list, sample_list_draw_list);
1767 _fix_accept_text();
1768
1769 widgets_samplelist[0].x = 5;
1770 widgets_samplelist[0].y = 13;
1771 widgets_samplelist[0].width = 30;
1772 widgets_samplelist[0].height = 35;
1773
1774 /* 1 -> 6 = middle column */
1775 create_thumbbar(widgets_samplelist + 1, 38, 16, 9, 1, 2, 7, update_values_in_song, 0, 64);
1776 create_thumbbar(widgets_samplelist + 2, 38, 23, 9, 1, 3, 7, update_values_in_song, 0, 64);
1777 create_toggle(widgets_samplelist + 3, 38, 30, 2, 4, 0, 7, 7, update_values_in_song);
1778 create_thumbbar(widgets_samplelist + 4, 38, 31, 9, 3, 5, 7, update_panning, 0, 64);
1779 create_thumbbar(widgets_samplelist + 5, 38, 39, 9, 4, 6, 15, update_values_in_song, 0, 64);
1780 create_thumbbar(widgets_samplelist + 6, 38, 46, 9, 5, 6, 19, update_values_in_song, 0, 32);
1781 /* 7 -> 14 = top right box */
1782 create_textentry(widgets_samplelist + 7, 64, 13, 13, 7, 8, 0, update_filename, NULL, 12);
1783 create_numentry(widgets_samplelist + 8, 64, 14, 7, 7, 9, 0,
1784 update_sample_speed, 0, 9999999, &sample_numentry_cursor_pos);
1785 create_menutoggle(widgets_samplelist + 9, 64, 15, 8, 10, 1, 0, 0,
1786 update_sample_loop_flags, loop_states);
1787 create_numentry(widgets_samplelist + 10, 64, 16, 7, 9, 11, 0,
1788 update_sample_loop_points, 0, 9999999, &sample_numentry_cursor_pos);
1789 create_numentry(widgets_samplelist + 11, 64, 17, 7, 10, 12, 0,
1790 update_sample_loop_points, 0, 9999999, &sample_numentry_cursor_pos);
1791 create_menutoggle(widgets_samplelist + 12, 64, 18, 11, 13, 1, 0, 0,
1792 update_sample_loop_flags, loop_states);
1793 create_numentry(widgets_samplelist + 13, 64, 19, 7, 12, 14, 0,
1794 update_sample_loop_points, 0, 9999999, &sample_numentry_cursor_pos);
1795 create_numentry(widgets_samplelist + 14, 64, 20, 7, 13, 15, 0,
1796 update_sample_loop_points, 0, 9999999, &sample_numentry_cursor_pos);
1797 /* 15 -> 18 = vibrato waveforms */
1798 create_togglebutton(widgets_samplelist + 15, 57, 36, 6, 14, 17, 5,
1799 16, 16, update_values_in_song, "\xb9\xba", 3, vibrato_waveforms);
1800 create_togglebutton(widgets_samplelist + 16, 67, 36, 6, 14, 18, 15,
1801 0, 0, update_values_in_song, "\xbd\xbe", 3, vibrato_waveforms);
1802 create_togglebutton(widgets_samplelist + 17, 57, 39, 6, 15, 19, 5,
1803 18, 18, update_values_in_song, "\xbb\xbc", 3, vibrato_waveforms);
1804 create_togglebutton(widgets_samplelist + 18, 67, 39, 6, 16, 19, 17,
1805 0, 0, update_values_in_song, "Random", 1, vibrato_waveforms);
1806 /* 19 = vibrato rate */
1807 create_thumbbar(widgets_samplelist + 19, 56, 46, 16, 17, 19, 0, update_values_in_song, 0, 255);
1808
1809 /* count how many formats there really are */
1810 num_save_formats = 0;
1811 while (sample_save_formats[num_save_formats].label)
1812 num_save_formats++;
1813 }
1814
1815