1 /*
2 Copyright (c) 2009-2010 Tero Lindeman (kometbomb)
3 
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation
6 files (the "Software"), to deal in the Software without
7 restriction, including without limitation the rights to use,
8 copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following
11 conditions:
12 
13 The above copyright notice and this permission notice shall be
14 included in all copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 OTHER DEALINGS IN THE SOFTWARE.
24 */
25 
26 #include "action.h"
27 #include "optimize.h"
28 #include "mused.h"
29 #include "gui/toolutil.h"
30 #include "view.h"
31 #include "event.h"
32 #include "gui/msgbox.h"
33 #include "version.h"
34 #include "klystron_version.h"
35 #include "gfx/gfx.h"
36 #include "theme.h"
37 #include "key.h"
38 #include "gui/menu.h"
39 #include "export.h"
40 #include <stdbool.h>
41 #include "gui/mouse.h"
42 #include "view/wavetableview.h"
43 #include "help.h"
44 #include <string.h>
45 
46 extern Mused mused;
47 extern GfxDomain *domain;
48 extern const Menu mainmenu[];
49 extern Menu pixelmenu[];
50 extern Menu patternlengthmenu[];
51 extern Menu oversamplemenu[];
52 
53 bool inside_undo = false;
54 
select_sequence_position(void * channel,void * position,void * unused)55 void select_sequence_position(void *channel, void *position, void* unused)
56 {
57 	if ((mused.flags & SONG_PLAYING) && (mused.flags & FOLLOW_PLAY_POSITION))
58 		return;
59 
60 	if (CASTPTR(int,channel) != -1)
61 		mused.current_sequencetrack = CASTPTR(int,channel);
62 
63 	if (CASTPTR(int,position) < mused.song.song_length)
64 		mused.current_sequencepos = CASTPTR(int,position);
65 
66 	mused.pattern_position = mused.current_patternpos = mused.current_sequencepos;
67 
68 	mused.focus = EDITSEQUENCE;
69 
70 	update_horiz_sliders();
71 }
72 
73 
select_pattern_param(void * id,void * position,void * track)74 void select_pattern_param(void *id, void *position, void *track)
75 {
76 	mused.current_patternx = CASTPTR(int,id);
77 	mused.current_sequencetrack = CASTPTR(int,track);
78 	mused.pattern_position = mused.current_sequencepos = CASTPTR(int,position);
79 
80 	mused.focus = EDITPATTERN;
81 
82 	update_horiz_sliders();
83 }
84 
85 
select_instrument_page(void * page,void * unused1,void * unused2)86 void select_instrument_page(void *page, void *unused1, void *unused2)
87 {
88 	mused.instrument_page = CASTPTR(int,page) * 10;
89 	set_info_message("Selected instrument bank %d-%d", mused.instrument_page, mused.instrument_page + 9);
90 }
91 
92 
93 
select_instrument(void * idx,void * relative,void * pagey)94 void select_instrument(void *idx, void *relative, void *pagey)
95 {
96 	if (pagey)
97 	{
98 		mused.current_instrument = mused.instrument_page + CASTPTR(int,idx);
99 	}
100 	else
101 	{
102 		if (relative)
103 			mused.current_instrument += CASTPTR(int,idx);
104 		else
105 			mused.current_instrument = CASTPTR(int,idx);
106 	}
107 
108 	if (mused.current_instrument >= NUM_INSTRUMENTS)
109 		mused.current_instrument = NUM_INSTRUMENTS-1;
110 	else if (mused.current_instrument < 0)
111 		mused.current_instrument = 0;
112 
113 }
114 
115 
select_wavetable(void * idx,void * unused1,void * unused2)116 void select_wavetable(void *idx, void *unused1, void *unused2)
117 {
118 	mused.selected_wavetable = CASTPTR(int,idx);
119 
120 	if (mused.selected_wavetable >= CYD_WAVE_MAX_ENTRIES)
121 		mused.selected_wavetable = CYD_WAVE_MAX_ENTRIES-1;
122 	else if (mused.selected_wavetable < 0)
123 		mused.selected_wavetable = 0;
124 
125 }
126 
127 
change_octave(void * delta,void * unused1,void * unused2)128 void change_octave(void *delta, void *unused1, void *unused2)
129 {
130 	mused.octave += CASTPTR(int,delta);
131 	if (mused.octave > 7) mused.octave = 7;
132 	else if (mused.octave < 0) mused.octave = 0;
133 }
134 
135 
change_oversample(void * oversample,void * unused1,void * unused2)136 void change_oversample(void *oversample, void *unused1, void *unused2)
137 {
138 	cyd_set_oversampling(&mused.cyd, CASTPTR(int,oversample));
139 
140 	mused.oversample = CASTPTR(int,oversample);
141 
142 	for (int i = 0 ; i < 4; ++i)
143 	{
144 		if (oversamplemenu[i].p1 == oversample)
145 			oversamplemenu[i].flags |= MENU_BULLET;
146 		else
147 			oversamplemenu[i].flags &= ~MENU_BULLET;
148 	}
149 }
150 
151 
change_song_rate(void * delta,void * unused1,void * unused2)152 void change_song_rate(void *delta, void *unused1, void *unused2)
153 {
154 	if (CASTPTR(int,delta) > 0)
155 	{
156 		if ((int)mused.song.song_rate + CASTPTR(int,delta) <= 0xff)
157 			mused.song.song_rate += CASTPTR(int,delta);
158 		else
159 			mused.song.song_rate = 0xff;
160 	}
161 	else if (CASTPTR(int,delta) < 0)
162 	{
163 		if ((int)mused.song.song_rate + CASTPTR(int,delta) >= 0x1)
164 			mused.song.song_rate += CASTPTR(int,delta);
165 		else
166 			mused.song.song_rate = 1;
167 	}
168 
169 	enable_callback(true);
170 }
171 
172 
change_time_signature(void * beat,void * unused1,void * unused2)173 void change_time_signature(void *beat, void *unused1, void *unused2)
174 {
175 	if (!beat)
176 	{
177 		mused.time_signature = (mused.time_signature & 0x00ff) | (((((mused.time_signature >> 8) + 1) & 0xff) % 17) << 8);
178 		if ((mused.time_signature & 0xff00) == 0) mused.time_signature |= 0x100;
179 	}
180 	else
181 	{
182 		mused.time_signature = (mused.time_signature & 0xff00) | ((((mused.time_signature & 0xff) + 1) & 0xff) % 17);
183 		if ((mused.time_signature & 0xff) == 0) mused.time_signature |= 1;
184 	}
185 }
186 
187 
play(void * from_cursor,void * unused1,void * unused2)188 void play(void *from_cursor, void *unused1, void *unused2)
189 {
190 	int pos = from_cursor ? mused.current_sequencepos : 0;
191 	mused.play_start_at = get_playtime_at(pos);
192 	enable_callback(true);
193 	mus_set_song(&mused.mus, &mused.song, pos);
194 	mused.flags |= SONG_PLAYING;
195 	if (mused.flags & STOP_EDIT_ON_PLAY) mused.flags &= ~EDIT_MODE;
196 }
197 
198 
play_position(void * unused1,void * unused2,void * unused3)199 void play_position(void *unused1, void *unused2, void *unused3)
200 {
201 	if (!(mused.flags & LOOP_POSITION))
202 	{
203 		int p = current_pattern(), len;
204 
205 		if (p < 0)
206 			len = mused.sequenceview_steps;
207 		else
208 			len = mused.song.pattern[p].num_steps;
209 
210 
211 		mused.flags |= LOOP_POSITION;
212 		mused.loop_store_length = mused.song.song_length;
213 		mused.loop_store_loop = mused.song.loop_point;
214 
215 		mused.song.song_length = mused.current_sequencepos + len;
216 		mused.song.loop_point = mused.current_sequencepos;
217 
218 		debug("%d Looping %d-%d", p, mused.current_sequencepos, mused.current_sequencepos + len);
219 
220 		play(MAKEPTR(1), 0, 0);
221 	}
222 }
223 
224 
stop(void * unused1,void * unused2,void * unused3)225 void stop(void *unused1, void *unused2, void *unused3)
226 {
227 	mus_set_song(&mused.mus, NULL, 0);
228 	if (mused.flags & LOOP_POSITION)
229 	{
230 		mused.song.song_length = mused.loop_store_length;
231 		mused.song.loop_point = mused.loop_store_loop;
232 	}
233 
234 	mused.flags &= ~(SONG_PLAYING | LOOP_POSITION);
235 }
236 
237 
change_song_speed(void * speed,void * delta,void * unused)238 void change_song_speed(void *speed, void *delta, void *unused)
239 {
240 	if (!speed)
241 	{
242 		if ((int)mused.song.song_speed + CASTPTR(int,delta) >= 1 && (int)mused.song.song_speed + CASTPTR(int,delta) <= 15)
243 			mused.song.song_speed += CASTPTR(int,delta);
244 	}
245 	else
246 	{
247 		if ((int)mused.song.song_speed2 + CASTPTR(int,delta) >= 1 && (int)mused.song.song_speed2 + CASTPTR(int,delta) <= 15)
248 		mused.song.song_speed2 += CASTPTR(int,delta);
249 	}
250 }
251 
252 
select_instrument_param(void * idx,void * unused1,void * unused2)253 void select_instrument_param(void *idx, void *unused1, void *unused2)
254 {
255 	mused.selected_param = CASTPTR(int,idx);
256 }
257 
258 
select_program_step(void * idx,void * digit,void * unused2)259 void select_program_step(void *idx, void *digit, void *unused2)
260 {
261 	mused.current_program_step = CASTPTR(int,idx);
262 	mused.editpos = CASTPTR(int, digit);
263 }
264 
265 
new_song_action(void * unused1,void * unused2,void * unused3)266 void new_song_action(void *unused1, void *unused2, void *unused3)
267 {
268 	if (confirm(domain, mused.slider_bevel, &mused.largefont, "Clear song and data?"))
269 	{
270 		stop(0,0,0);
271 		new_song();
272 	}
273 }
274 
275 
kill_instrument(void * unused1,void * unused2,void * unused3)276 void kill_instrument(void *unused1, void *unused2, void *unused3)
277 {
278 	cyd_lock(&mused.cyd, 1);
279 	mus_get_default_instrument(&mused.song.instrument[mused.current_instrument]);
280 	cyd_lock(&mused.cyd, 0);
281 }
282 
283 
generic_action(void * func,void * unused1,void * unused2)284 void generic_action(void *func, void *unused1, void *unused2)
285 {
286 	mus_set_song(&mused.mus, NULL, 0);
287 	cyd_lock(&mused.cyd, 1);
288 
289 	((void *(*)(void))func)(); /* I love the smell of C in the morning */
290 
291 	cyd_lock(&mused.cyd, 0);
292 }
293 
294 
quit_action(void * unused1,void * unused2,void * unused3)295 void quit_action(void *unused1, void *unused2, void *unused3)
296 {
297 	mused.done = 1;
298 }
299 
300 
change_mode_action(void * mode,void * unused1,void * unused2)301 void change_mode_action(void *mode, void *unused1, void *unused2)
302 {
303 	change_mode(CASTPTR(int,mode));
304 }
305 
306 
enable_channel(void * channel,void * unused1,void * unused2)307 void enable_channel(void *channel, void *unused1, void *unused2)
308 {
309 	debug("Toggle chn %d", CASTPTR(int,channel));
310 	mused.mus.channel[CASTPTR(int,channel)].flags ^= MUS_CHN_DISABLED;
311 
312 	set_info_message("%s channel %d", (mused.mus.channel[CASTPTR(int,channel)].flags & MUS_CHN_DISABLED) ? "Muted" : "Unmuted", CASTPTR(int,channel));
313 }
314 
315 
solo_channel(void * _channel,void * unused1,void * unused2)316 void solo_channel(void *_channel, void *unused1, void *unused2)
317 {
318 	int c = 0;
319 	int channel = CASTPTR(int,channel);
320 
321 	if (channel == -1)
322 		channel = mused.current_sequencetrack;
323 
324 	for (int i = 0 ; i < MUS_MAX_CHANNELS ; ++i)
325 		if (!(mused.mus.channel[i].flags & MUS_CHN_DISABLED))
326 			++c;
327 
328 	if (c == 1 && !(mused.mus.channel[channel].flags & MUS_CHN_DISABLED))
329 	{
330 		debug("Unmuted all");
331 		for (int i = 0 ; i < MUS_MAX_CHANNELS ; ++i)
332 			mused.mus.channel[i].flags &= ~MUS_CHN_DISABLED;
333 
334 		set_info_message("Unmuted all channels");
335 	}
336 	else
337 	{
338 		debug("Solo chn %d", CASTPTR(int,channel));
339 		for (int i = 0 ; i < MUS_MAX_CHANNELS ; ++i)
340 			mused.mus.channel[i].flags |= MUS_CHN_DISABLED;
341 
342 		mused.mus.channel[channel].flags &= ~MUS_CHN_DISABLED;
343 
344 		set_info_message("Channel %d solo", channel);
345 	}
346 }
347 
348 
unmute_all_action(void * unused1,void * unused2,void * unused3)349 void unmute_all_action(void *unused1, void *unused2, void *unused3)
350 {
351 	for(int i = 0 ; i < MUS_MAX_CHANNELS ; ++i)
352 		mused.mus.channel[i].flags &= ~MUS_CHN_DISABLED;
353 }
354 
355 
select_all(void * unused1,void * unused2,void * unused3)356 void select_all(void *unused1, void *unused2, void *unused3)
357 {
358 	switch (mused.focus)
359 	{
360 		case EDITPATTERN:
361 			mused.selection.start = 0;
362 			mused.selection.end = mused.song.pattern[current_pattern(NULL)].num_steps;
363 			break;
364 
365 		case EDITPROG:
366 			mused.selection.start = 0;
367 			mused.selection.end = MUS_PROG_LEN;
368 			break;
369 
370 		case EDITSEQUENCE:
371 			mused.selection.start = 0;
372 			mused.selection.end = mused.song.song_length;
373 			break;
374 	}
375 }
376 
377 
clear_selection(void * unused1,void * unused2,void * unused3)378 void clear_selection(void *unused1, void *unused2, void *unused3)
379 {
380 	mused.selection.start = 0;
381 	mused.selection.end = 0;
382 }
383 
384 
cycle_focus(void * _views,void * _focus,void * _mode)385 void cycle_focus(void *_views, void *_focus, void *_mode)
386 {
387 	View **viewlist = _views;
388 	int *focus = _focus, *mode = _mode;
389 	View *views = viewlist[*mode];
390 
391 	int i;
392 	for (i = 0 ; views[i].handler ; ++i)
393 	{
394 		if (views[i].focus == *focus) break;
395 	}
396 
397 	if (!views[i].handler) return;
398 
399 	int next;
400 
401 	for (next = i + 1 ; i != next ; ++next)
402 	{
403 		if (views[next].handler == NULL)
404 		{
405 			next = -1;
406 			continue;
407 		}
408 
409 		if (views[next].focus != -1 && views[next].focus != *focus)
410 		{
411 			*focus = views[next].focus;
412 			break;
413 		}
414 	}
415 }
416 
417 
change_song_length(void * delta,void * unused1,void * unused2)418 void change_song_length(void *delta, void *unused1, void *unused2)
419 {
420 	int l = mused.song.song_length;
421 	l += CASTPTR(int, delta);
422 	l = l - (l % mused.sequenceview_steps);
423 
424 	mused.song.song_length = my_max(0, my_min(0xfffe, l));
425 }
426 
427 
change_loop_point(void * delta,void * unused1,void * unused2)428 void change_loop_point(void *delta, void *unused1, void *unused2)
429 {
430 	if (CASTPTR(int,delta) < 0)
431 	{
432 		if (mused.song.loop_point >= -CASTPTR(int,delta))
433 			mused.song.loop_point += CASTPTR(int,delta);
434 		else
435 			mused.song.loop_point = 0;
436 	}
437 	else if (CASTPTR(int,delta) > 0)
438 	{
439 		mused.song.loop_point += CASTPTR(int,delta);
440 		if (mused.song.loop_point >= mused.song.song_length)
441 			mused.song.loop_point = mused.song.song_length;
442 	}
443 }
444 
445 
change_seq_steps(void * delta,void * unused1,void * unused2)446 void change_seq_steps(void *delta, void *unused1, void *unused2)
447 {
448 	if (CASTPTR(int,delta) < 0)
449 	{
450 		if (mused.sequenceview_steps > -CASTPTR(int,delta))
451 		{
452 			mused.sequenceview_steps += CASTPTR(int,delta);
453 		}
454 		else
455 			mused.sequenceview_steps = 1;
456 	}
457 	else if (CASTPTR(int,delta) > 0)
458 	{
459 		if (mused.sequenceview_steps == 1 && CASTPTR(int,delta) > 1)
460 			mused.sequenceview_steps = 0;
461 
462 		if (mused.sequenceview_steps < 128)
463 		{
464 			mused.sequenceview_steps += CASTPTR(int,delta);
465 		}
466 		else
467 			mused.sequenceview_steps = 128;
468 	}
469 
470 	mused.current_sequencepos = (mused.current_sequencepos/mused.sequenceview_steps) * mused.sequenceview_steps;
471 
472 	if (mused.flags & LOCK_SEQUENCE_STEP_AND_PATTERN_LENGTH)
473 		change_default_pattern_length(MAKEPTR(mused.sequenceview_steps), NULL, NULL);
474 }
475 
476 
show_about_box(void * unused1,void * unused2,void * unused3)477 void show_about_box(void *unused1, void *unused2, void *unused3)
478 {
479 	msgbox(domain, mused.slider_bevel, &mused.largefont, VERSION_STRING "\n" KLYSTRON_VERSION_STRING, MB_OK);
480 }
481 
482 
change_channels(void * delta,void * unused1,void * unused2)483 void change_channels(void *delta, void *unused1, void *unused2)
484 {
485 	if (CASTPTR(int,delta) < 0 && mused.song.num_channels > 1)
486 	{
487 		set_channels(mused.song.num_channels - 1);
488 	}
489 	else if (CASTPTR(int,delta) > 0 && mused.song.num_channels < MUS_MAX_CHANNELS)
490 	{
491 		set_channels(mused.song.num_channels + 1);
492 	}
493 }
494 
495 
change_master_volume(void * delta,void * unused1,void * unused2)496 void change_master_volume(void *delta, void *unused1, void *unused2)
497 {
498 	if (CASTPTR(int,delta) < 0 && mused.song.master_volume > 0)
499 	{
500 		mused.mus.volume = --mused.song.master_volume;
501 	}
502 	else if (CASTPTR(int,delta) > 0 && mused.song.master_volume < MAX_VOLUME)
503 	{
504 		mused.mus.volume = ++mused.song.master_volume;
505 	}
506 }
507 
508 
begin_selection_action(void * unused1,void * unused2,void * unused3)509 void begin_selection_action(void *unused1, void *unused2, void *unused3)
510 {
511 	switch (mused.focus)
512 	{
513 		case EDITPATTERN:
514 		begin_selection(current_patternstep());
515 		break;
516 
517 		case EDITSEQUENCE:
518 		begin_selection(mused.current_sequencepos);
519 		break;
520 
521 		case EDITPROG:
522 		begin_selection(mused.current_program_step);
523 		break;
524 	}
525 }
526 
527 
end_selection_action(void * unused1,void * unused2,void * unused3)528 void end_selection_action(void *unused1, void *unused2, void *unused3)
529 {
530 	switch (mused.focus)
531 	{
532 		case EDITPATTERN:
533 		select_range(current_patternstep());
534 		break;
535 
536 		case EDITSEQUENCE:
537 		select_range(mused.current_sequencepos);
538 		break;
539 
540 		case EDITPROG:
541 		select_range(mused.current_program_step);
542 		break;
543 	}
544 }
545 
546 
toggle_pixel_scale(void * a,void * b,void * c)547 void toggle_pixel_scale(void *a, void*b, void*c)
548 {
549 	change_pixel_scale(MAKEPTR(((domain->scale) & 3) + 1), 0, 0);
550 }
551 
552 
change_pixel_scale(void * scale,void * b,void * c)553 void change_pixel_scale(void *scale, void*b, void*c)
554 {
555 	mused.pixel_scale = CASTPTR(int,scale);
556 	domain->screen_w = my_max(320, mused.window_w / mused.pixel_scale);
557 	domain->screen_h = my_max(240, mused.window_h / mused.pixel_scale);
558 	domain->scale = mused.pixel_scale;
559 	gfx_domain_update(domain, true);
560 
561 	set_scaled_cursor();
562 
563 	for (int i = 0 ; i < 4; ++i)
564 	{
565 		if (pixelmenu[i].p1 == scale)
566 			pixelmenu[i].flags |= MENU_BULLET;
567 		else
568 			pixelmenu[i].flags &= ~MENU_BULLET;
569 	}
570 }
571 
572 
toggle_fullscreen(void * a,void * b,void * c)573 void toggle_fullscreen(void *a, void*b, void*c)
574 {
575 	SDL_Event e;
576 	e.button.button = SDL_BUTTON_LEFT;
577 	mouse_released(&e);
578 	mused.flags ^= FULLSCREEN;
579 	change_fullscreen(0,0,0);
580 }
581 
582 
change_fullscreen(void * a,void * b,void * c)583 void change_fullscreen(void *a, void*b, void*c)
584 {
585 	domain->fullscreen = (mused.flags & FULLSCREEN) != 0;
586 
587 	if (!(mused.flags & FULLSCREEN))
588 	{
589 		domain->screen_w = mused.window_w / domain->scale;
590 		domain->screen_h = mused.window_h / domain->scale;
591 	}
592 
593 	gfx_domain_update(domain, true);
594 }
595 
596 
toggle_render_to_texture(void * a,void * b,void * c)597 void toggle_render_to_texture(void *a, void*b, void*c)
598 {
599 	SDL_Event e;
600 	e.button.button = SDL_BUTTON_LEFT;
601 	mouse_released(&e);
602 	mused.flags ^= DISABLE_RENDER_TO_TEXTURE;
603 	change_render_to_texture(0,0,0);
604 }
605 
606 
toggle_mouse_cursor(void * a,void * b,void * c)607 void toggle_mouse_cursor(void *a, void*b, void*c)
608 {
609 	SDL_Event e;
610 	e.button.button = SDL_BUTTON_LEFT;
611 	mouse_released(&e);
612 	mused.flags ^= USE_SYSTEM_CURSOR;
613 	set_scaled_cursor();
614 }
615 
616 
change_render_to_texture(void * a,void * b,void * c)617 void change_render_to_texture(void *a, void*b, void*c)
618 {
619 	domain->flags = (domain->flags & ~GFX_DOMAIN_DISABLE_RENDER_TO_TEXTURE) | ((mused.flags & DISABLE_RENDER_TO_TEXTURE) ? GFX_DOMAIN_DISABLE_RENDER_TO_TEXTURE : 0);
620 	gfx_domain_update(domain, true);
621 }
622 
623 
load_theme_action(void * name,void * b,void * c)624 void load_theme_action(void *name, void*b, void*c)
625 {
626 	load_theme((char*)name);
627 }
628 
629 
load_keymap_action(void * name,void * b,void * c)630 void load_keymap_action(void *name, void*b, void*c)
631 {
632 	load_keymap((char*)name);
633 }
634 
635 
change_timesig(void * delta,void * b,void * c)636 void change_timesig(void *delta, void *b, void *c)
637 {
638 	// http://en.wikipedia.org/wiki/Time_signature says the following signatures are common.
639 	// I'm a 4/4 or 3/4 man myself so I'll trust the article :)
640 
641 	static const Uint16 sigs[] = { 0x0404, 0x0304, 0x0604, 0x0308, 0x0608, 0x0908, 0x0c08 };
642 	int i;
643 	for (i = 0 ; i < sizeof(sigs) / sizeof(sigs[0]) ; ++i)
644 	{
645 		if (sigs[i] == mused.time_signature)
646 			break;
647 	}
648 
649 	i += CASTPTR(int,delta);
650 
651 	if (i >= (int)(sizeof(sigs) / sizeof(sigs[0]))) i = 0;
652 	if (i < 0) i = sizeof(sigs) / sizeof(sigs[0]) - 1;
653 	mused.time_signature = sigs[i];
654 }
655 
656 
export_wav_action(void * a,void * b,void * c)657 void export_wav_action(void *a, void*b, void*c)
658 {
659 	char def[1000];
660 
661 	if (strlen(mused.previous_song_filename) == 0)
662 	{
663 		snprintf(def, sizeof(def), "%s.wav", mused.song.title);
664 	}
665 	else
666 	{
667 		strncpy(def, mused.previous_export_filename, sizeof(mused.previous_export_filename) - 1);
668 	}
669 
670 	char filename[5000];
671 
672 	if (open_dialog_fn("wb", "Export .WAV", "wav", domain, mused.slider_bevel, &mused.largefont, &mused.smallfont, def, filename, sizeof(filename)))
673 	{
674 		strncpy(mused.previous_export_filename, filename, sizeof(mused.previous_export_filename) - 1);
675 
676 		FILE *f = fopen(filename, "wb");
677 
678 		if (f)
679 		{
680 			export_wav(&mused.song, mused.mus.cyd->wavetable_entries, f, -1);
681 			// f is closed inside of export_wav (inside of ww_finish)
682 		}
683 	}
684 }
685 
686 
export_channels_action(void * a,void * b,void * c)687 void export_channels_action(void *a, void*b, void*c)
688 {
689 	char def[1000];
690 
691 	if (strlen(mused.previous_song_filename) == 0)
692 	{
693 		snprintf(def, sizeof(def), "%s.wav", mused.song.title);
694 	}
695 	else
696 	{
697 		strncpy(def, mused.previous_export_filename, sizeof(mused.previous_export_filename) - 1);
698 	}
699 
700 	char filename[5000];
701 
702 	if (open_dialog_fn("wb", "Export .WAV", "wav", domain, mused.slider_bevel, &mused.largefont, &mused.smallfont, def, filename, sizeof(filename)))
703 	{
704 		strncpy(mused.previous_export_filename, filename, sizeof(mused.previous_export_filename) - 1);
705 
706 		for (int i = 0 ; i < mused.song.num_channels ; ++i)
707 		{
708 			char c_filename[1500], tmp[1000];
709 			strncpy(tmp, filename, sizeof(tmp) - 1);
710 
711 			for (int c = strlen(tmp) - 1 ; c >= 0 ; --c)
712 			{
713 				if (tmp[c] == '.')
714 				{
715 					tmp[c] = '\0';
716 					break;
717 				}
718 			}
719 
720 			snprintf(c_filename, sizeof(c_filename) - 1, "%s-%02d.wav", tmp, i);
721 
722 			debug("Exporting channel %d to %s", i, c_filename);
723 
724 			FILE *f = fopen(c_filename, "wb");
725 
726 			if (f)
727 			{
728 				export_wav(&mused.song, mused.mus.cyd->wavetable_entries, f, i);
729 				// f is closed inside of export_wav (inside of ww_finish)
730 			}
731 		}
732 	}
733 }
734 
735 
736 
do_undo(void * a,void * b,void * c)737 void do_undo(void *a, void*b, void*c)
738 {
739 	UndoFrame *frame = a ? undo(&mused.redo) : undo(&mused.undo);
740 
741 	debug("%s frame %p", a ? "Redo" : "Undo", frame);
742 
743 	if (!frame) return;
744 
745 
746 
747 	if (!a)
748 	{
749 		UndoStack tmp = mused.redo;
750 		mused.redo = mused.undo;
751 		mused.undo = tmp;
752 	}
753 
754 	switch (frame->type)
755 	{
756 		case UNDO_PATTERN:
757 			undo_store_pattern(&mused.undo, frame->event.pattern.idx, &mused.song.pattern[frame->event.pattern.idx], mused.modified);
758 
759 			resize_pattern(&mused.song.pattern[frame->event.pattern.idx], frame->event.pattern.n_steps);
760 			memcpy(mused.song.pattern[frame->event.pattern.idx].step, frame->event.pattern.step, frame->event.pattern.n_steps * sizeof(frame->event.pattern.step[0]));
761 			break;
762 
763 		case UNDO_SEQUENCE:
764 			mused.current_sequencetrack = frame->event.sequence.channel;
765 
766 			undo_store_sequence(&mused.undo, mused.current_sequencetrack, mused.song.sequence[mused.current_sequencetrack], mused.song.num_sequences[mused.current_sequencetrack], mused.modified);
767 
768 			mused.song.num_sequences[mused.current_sequencetrack] = frame->event.sequence.n_seq;
769 
770 			memcpy(mused.song.sequence[mused.current_sequencetrack], frame->event.sequence.seq, frame->event.sequence.n_seq * sizeof(frame->event.sequence.seq[0]));
771 			break;
772 
773 		case UNDO_MODE:
774 			change_mode(frame->event.mode.old_mode);
775 			mused.focus = frame->event.mode.focus;
776 			break;
777 
778 		case UNDO_INSTRUMENT:
779 			mused.current_instrument = frame->event.instrument.idx;
780 
781 			undo_store_instrument(&mused.undo, mused.current_instrument, &mused.song.instrument[mused.current_instrument], mused.modified);
782 
783 			memcpy(&mused.song.instrument[mused.current_instrument], &frame->event.instrument.instrument, sizeof(frame->event.instrument.instrument));
784 
785 			break;
786 
787 		case UNDO_FX:
788 			mused.fx_bus = frame->event.fx.idx;
789 
790 			undo_store_fx(&mused.undo, mused.fx_bus, &mused.song.fx[mused.fx_bus], mused.song.multiplex_period, mused.modified);
791 
792 			memcpy(&mused.song.fx[mused.fx_bus], &frame->event.fx.fx, sizeof(frame->event.fx.fx));
793 			mused.song.multiplex_period = frame->event.fx.multiplex_period;
794 			mus_set_fx(&mused.mus, &mused.song);
795 			break;
796 
797 		case UNDO_SONGINFO:
798 		{
799 			undo_store_songinfo(&mused.undo, &mused.song, mused.modified);
800 
801 			MusSong *song = &mused.song;
802 			song->song_length = frame->event.songinfo.song_length;
803 			mused.sequenceview_steps = song->sequence_step = frame->event.songinfo.sequence_step;
804 			song->loop_point = frame->event.songinfo.loop_point;
805 			song->song_speed = frame->event.songinfo.song_speed;
806 			song->song_speed2 = frame->event.songinfo.song_speed2;
807 			song->song_rate = frame->event.songinfo.song_rate;
808 			song->time_signature = frame->event.songinfo.time_signature;
809 			song->flags = frame->event.songinfo.flags;
810 			song->num_channels = frame->event.songinfo.num_channels;
811 			strcpy(song->title, frame->event.songinfo.title);
812 			song->master_volume = frame->event.songinfo.master_volume;
813 			memcpy(song->default_volume, frame->event.songinfo.default_volume, sizeof(frame->event.songinfo.default_volume));
814 			memcpy(song->default_panning, frame->event.songinfo.default_panning, sizeof(frame->event.songinfo.default_panning));
815 		}
816 		break;
817 
818 		case UNDO_WAVE_PARAM:
819 		{
820 			mused.selected_wavetable = frame->event.wave_param.idx;
821 
822 			undo_store_wave_param(&mused.undo, mused.selected_wavetable, &mused.mus.cyd->wavetable_entries[mused.selected_wavetable], mused.modified);
823 
824 			CydWavetableEntry *entry = &mused.mus.cyd->wavetable_entries[mused.selected_wavetable];
825 			entry->flags = frame->event.wave_param.flags;
826 			entry->sample_rate = frame->event.wave_param.sample_rate;
827 			entry->samples = frame->event.wave_param.samples;
828 			entry->loop_begin = frame->event.wave_param.loop_begin;
829 			entry->loop_end = frame->event.wave_param.loop_end;
830 			entry->base_note = frame->event.wave_param.base_note;
831 		}
832 		break;
833 
834 		case UNDO_WAVE_DATA:
835 		{
836 			mused.selected_wavetable = frame->event.wave_param.idx;
837 
838 			undo_store_wave_data(&mused.undo, mused.selected_wavetable, &mused.mus.cyd->wavetable_entries[mused.selected_wavetable], mused.modified);
839 
840 			CydWavetableEntry *entry = &mused.mus.cyd->wavetable_entries[mused.selected_wavetable];
841 			entry->data = realloc(entry->data, frame->event.wave_data.length * sizeof(entry->data[0]));
842 			memcpy(entry->data, frame->event.wave_data.data, frame->event.wave_data.length * sizeof(entry->data[0]));
843 			entry->samples = frame->event.wave_data.length;
844 			entry->sample_rate = frame->event.wave_data.sample_rate;
845 			entry->samples = frame->event.wave_data.samples;
846 			entry->loop_begin = frame->event.wave_data.loop_begin;
847 			entry->loop_end = frame->event.wave_data.loop_end;
848 			entry->flags = frame->event.wave_data.flags;
849 			entry->base_note = frame->event.wave_data.base_note;
850 
851 			invalidate_wavetable_view();
852 		}
853 		break;
854 
855 		case UNDO_WAVE_NAME:
856 		{
857 			mused.selected_wavetable = frame->event.wave_name.idx;
858 
859 			undo_store_wave_name(&mused.undo, mused.selected_wavetable, mused.song.wavetable_names[mused.selected_wavetable], mused.modified);
860 
861 			strcpy(mused.song.wavetable_names[mused.selected_wavetable], frame->event.wave_name.name);
862 
863 			invalidate_wavetable_view();
864 		}
865 		break;
866 
867 		default: warning("Undo type %d not handled", frame->type); break;
868 	}
869 
870 	mused.modified = frame->modified;
871 
872 	if (!a)
873 	{
874 		UndoStack tmp = mused.redo;
875 		mused.redo = mused.undo;
876 		mused.undo = tmp;
877 	}
878 
879 
880 	mused.last_snapshot_a = -1;
881 
882 /*#ifdef DEBUG
883 	undo_show_stack(&mused.undo);
884 	undo_show_stack(&mused.redo);
885 #endif*/
886 
887 	undo_destroy_frame(frame);
888 }
889 
890 
kill_wavetable_entry(void * a,void * b,void * c)891 void kill_wavetable_entry(void *a, void*b, void*c)
892 {
893 	snapshot(S_T_WAVE_DATA);
894 	cyd_wave_entry_init(&mused.mus.cyd->wavetable_entries[mused.selected_wavetable], NULL, 0, 0, 0, 0, 0);
895 }
896 
897 
open_menu_action(void * a,void * b,void * c)898 void open_menu_action(void*a,void*b,void*c)
899 {
900 	my_open_menu(mainmenu, NULL);
901 }
902 
903 
flip_bit_action(void * a,void * b,void * c)904 void flip_bit_action(void *a, void *b, void *c)
905 {
906 	*(Uint32*)a ^= CASTPTR(Uint32,b);
907 }
908 
909 
set_note_jump(void * steps,void * unused1,void * unused2)910 void set_note_jump(void *steps, void *unused1, void *unused2)
911 {
912 	mused.note_jump = CASTPTR(int, steps);
913 
914 	set_info_message("Note jump set to %d", mused.note_jump);
915 }
916 
917 
change_default_pattern_length(void * length,void * unused1,void * unused2)918 void change_default_pattern_length(void *length, void *unused1, void *unused2)
919 {
920 	for (int i = 0 ; i < NUM_PATTERNS ; ++i)
921 	{
922 		if (mused.song.pattern[i].num_steps == mused.default_pattern_length && is_pattern_empty(&mused.song.pattern[i]))
923 		{
924 			resize_pattern(&mused.song.pattern[i], CASTPTR(int,length));
925 		}
926 	}
927 
928 	mused.sequenceview_steps = mused.default_pattern_length = CASTPTR(int,length);
929 
930 	for (Menu *m = patternlengthmenu ; m->text ; ++m)
931 	{
932 		if (CASTPTR(int, m->p1) == mused.default_pattern_length)
933 			m->flags |= MENU_BULLET;
934 		else
935 			m->flags &= ~MENU_BULLET;
936 	}
937 }
938 
939 
toggle_visualizer(void * unused1,void * unused2,void * unused3)940 void toggle_visualizer(void *unused1, void *unused2, void *unused3)
941 {
942 	++mused.current_visualizer;
943 
944 	if (mused.current_visualizer >= VIS_NUM_TOTAL)
945 		mused.current_visualizer = 0;
946 }
947 
948 
change_visualizer_action(void * vis,void * unused1,void * unused2)949 void change_visualizer_action(void *vis, void *unused1, void *unused2)
950 {
951 	change_visualizer(CASTPTR(int,vis));
952 }
953 
954 
open_help(void * unused0,void * unused1,void * unused2)955 void open_help(void *unused0, void *unused1, void *unused2)
956 {
957 	cyd_lock(&mused.cyd, 0);
958 	open_help_no_lock(NULL, NULL, NULL);
959 	cyd_lock(&mused.cyd, 1);
960 }
961 
962 
open_help_no_lock(void * unused0,void * unused1,void * unused2)963 void open_help_no_lock(void *unused0, void *unused1, void *unused2)
964 {
965 	helpbox("Help", domain, mused.slider_bevel, &mused.largefont, &mused.smallfont);
966 }
967 
968 
toggle_follow_play_position(void * unused1,void * unused2,void * unused3)969 void toggle_follow_play_position(void *unused1, void *unused2, void *unused3)
970 {
971 	mused.flags ^= FOLLOW_PLAY_POSITION;
972 
973 	if (mused.flags & FOLLOW_PLAY_POSITION)
974 		set_info_message("Following song position");
975 	else
976 		set_info_message("Not following song position");
977 }
978 
979 
open_recent_file(void * path,void * b,void * c)980 void open_recent_file(void *path, void *b, void *c)
981 {
982 	debug("Opening recent file %s", (char*)path);
983 	FILE *f = fopen(path, "rb");
984 
985 	if (f)
986 	{
987 		open_song(f);
988 		fclose(f);
989 
990 		// Need to copy this var because update_recent_files_list()
991 		// might free the string in *path...
992 
993 		char *temp = strdup(path);
994 		update_recent_files_list(temp);
995 		free(temp);
996 	}
997 	else
998 	{
999 		warning("Could not open %s", (char*)path);
1000 	}
1001 }
1002