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