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