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 "mused.h"
27 #include "util/bundle.h"
28 #include "gfx/font.h"
29 #include "gfx/gfx.h"
30 #include "action.h"
31 #include "event.h"
32 #include "theme.h"
33 #include "undo.h"
34 #include "edit.h"
35 #include "key.h"
36 #include "view/wavetableview.h"
37 #include "zap.h"
38 #include "diskop.h"
39 #include <stdarg.h>
40 #include <string.h>
41
42 extern Mused mused;
43 extern Menu editormenu[];
44 extern Menu analyzermenu[];
45
set_edit_buffer(char * buffer,size_t size)46 void set_edit_buffer(char *buffer, size_t size)
47 {
48 if (mused.edit_backup_buffer)
49 free(mused.edit_backup_buffer);
50
51 mused.edit_backup_buffer = strdup(buffer);
52 mused.edit_buffer = buffer;
53 mused.edit_buffer_size = size;
54 mused.editpos = strlen(mused.edit_buffer);
55 change_mode(EDITBUFFER);
56 }
57
58
change_mode(int newmode)59 void change_mode(int newmode)
60 {
61 if (newmode == EDITBUFFER)
62 SDL_StartTextInput();
63 else
64 SDL_StopTextInput();
65
66 if (newmode < VIRTUAL_MODE && mused.mode != MENU)
67 {
68 gfx_clear(domain, 0);
69 }
70
71 if (mused.mode < VIRTUAL_MODE)
72 {
73 for (int i = 0 ; editormenu[i].parent ; ++i)
74 editormenu[i].flags = (editormenu[i].flags & ~MENU_BULLET) | (mused.mode == CASTPTR(int, editormenu[i].p1) ? MENU_BULLET : 0);
75 mused.prev_mode = mused.mode;
76
77 mused.cursor.w = mused.cursor.h = mused.cursor_target.w = mused.cursor_target.h = 0;
78
79 if (newmode != mused.mode && newmode < VIRTUAL_MODE) snapshot(S_T_MODE);
80 }
81
82 switch (newmode)
83 {
84 case EDITFX:
85 if (mused.mode == EDITINSTRUMENT)
86 {
87 mused.fx_bus = mused.song.instrument[mused.current_instrument].fx_bus;
88 }
89
90 break;
91
92 case EDITWAVETABLE:
93 if (mused.mode == EDITINSTRUMENT)
94 {
95 mused.selected_wavetable = mused.song.instrument[mused.current_instrument].wavetable_entry;
96 }
97
98 break;
99
100 case EDITCLASSIC:
101 case EDITPATTERN:
102 if (mused.mode == EDITBUFFER) break;
103 if (mused.mode == EDITSEQUENCE || (mused.mode == MENU && !mused.single_pattern_edit) || newmode == EDITCLASSIC)
104 {
105 slider_move_position(&mused.current_sequencetrack, &mused.pattern_horiz_position, &mused.pattern_horiz_slider_param, 0);
106 //slider_move_position(&mused.current_patternstep, &mused.pattern_position, &mused.pattern_slider_param, 0, mused.song.pattern[mused.current_pattern].num_steps);
107 }
108 else
109 {
110 mused.single_pattern_edit = 1;
111 mused.current_patternx = 0;
112 }
113 break;
114 }
115
116 mused.mode = newmode;
117 mused.focus = newmode;
118
119 if (mused.focus == EDITCLASSIC)
120 mused.focus = EDITPATTERN;
121
122 if (mused.mode == EDITCLASSIC || mused.mode == EDITPATTERN)
123 {
124 mused.selected_param = 0;
125 }
126 }
127
128
clear_pattern(MusPattern * pat)129 void clear_pattern(MusPattern *pat)
130 {
131 snapshot(S_T_PATTERN);
132 clear_pattern_range(pat, 0, pat->num_steps);
133 }
134
135
clear_pattern_range(MusPattern * pat,int first,int last)136 void clear_pattern_range(MusPattern *pat, int first, int last)
137 {
138 for (int i = my_max(0, first) ; i < my_min(pat->num_steps, last) ; ++i)
139 {
140 pat->step[i].note = MUS_NOTE_NONE;
141 pat->step[i].instrument = MUS_NOTE_NO_INSTRUMENT;
142 pat->step[i].ctrl = 0;
143 pat->step[i].command = 0;
144 pat->step[i].volume = MUS_NOTE_NO_VOLUME;
145 }
146 }
147
148
new_song()149 void new_song()
150 {
151 debug("New song");
152
153 zap_instruments(MAKEPTR(1), NULL, NULL);
154
155 zap_sequence(MAKEPTR(1), NULL, NULL);
156
157 mused.song.master_volume = MAX_VOLUME;
158 mused.song.num_channels = 4;
159 mused.song.num_instruments = NUM_INSTRUMENTS;
160 mused.song.num_patterns = NUM_PATTERNS;
161 mused.song.num_wavetables = CYD_WAVE_MAX_ENTRIES;
162 mused.song.song_speed = 6;
163 mused.song.song_speed2 = 6;
164 mused.song.song_rate = 50;
165
166 memset(mused.song.title, 0, sizeof(mused.song.title));
167 strcpy(mused.previous_song_filename, "");
168 strcpy(mused.previous_export_filename, "");
169
170 zap_fx(MAKEPTR(1), NULL, NULL);
171
172 zap_wavetable(MAKEPTR(1), NULL, NULL);
173
174 undo_deinit(&mused.undo);
175 undo_init(&mused.undo);
176 undo_deinit(&mused.redo);
177 undo_init(&mused.redo);
178
179 mused.modified = false;
180
181 set_channels(mused.song.num_channels);
182 }
183
184
kt_default_instrument(MusInstrument * inst)185 void kt_default_instrument(MusInstrument *inst)
186 {
187 mus_get_default_instrument(inst);
188 }
189
190
resize_pattern(MusPattern * pattern,Uint16 new_size)191 void resize_pattern(MusPattern * pattern, Uint16 new_size)
192 {
193 int old_steps = pattern->num_steps;
194
195 if (new_size == old_steps)
196 return;
197
198 pattern->num_steps = new_size;
199
200 if (new_size > old_steps)
201 {
202 pattern->step = realloc(pattern->step, sizeof(pattern->step[0]) * (size_t)new_size);
203 clear_pattern_range(pattern, old_steps, new_size);
204 }
205
206
207 if (mused.focus == EDITPATTERN)
208 {
209 mused.selection.start = my_min(mused.selection.start, pattern->num_steps - 1);
210 mused.selection.end = my_min(mused.selection.end, pattern->num_steps - 1);
211 }
212 }
213
214
default_settings()215 void default_settings()
216 {
217 mused.window_w = 640;
218 mused.window_h = 480;
219 mused.pixel_scale = 1;
220 }
221
222
init(MusInstrument * instrument,MusPattern * pattern,MusSeqPattern sequence[MUS_MAX_CHANNELS][NUM_SEQUENCES],MusChannel * channel)223 void init(MusInstrument *instrument, MusPattern *pattern, MusSeqPattern sequence[MUS_MAX_CHANNELS][NUM_SEQUENCES], MusChannel *channel)
224 {
225 debug("init");
226
227 memset(&mused, 0, sizeof(mused));
228
229 set_info_message("Welcome to klystrack!");
230
231 default_settings();
232
233 mused.flags = MULTICHANNEL_PREVIEW|ANIMATE_CURSOR|EDIT_MODE|SHOW_LOGO|FOLLOW_PLAY_POSITION|
234 MULTIKEY_JAMMING|START_WITH_TEMPLATE|EDIT_SEQUENCE_DIGITS;
235
236 // Rendering to the intermediary texture is disabled by default on Linux systems
237 // as it causes flashing of screen
238 #ifdef CONFIG_DEFAULT_DISABLE_RENDER_TO_TEXTURE
239 mused.flags |= DISABLE_RENDER_TO_TEXTURE;
240 #endif
241
242 mused.visible_columns = VC_INSTRUMENT | VC_COMMAND;
243 mused.done = 0;
244 mused.octave = 4;
245 mused.note_jump = 1;
246 mused.current_instrument = 0;
247 mused.selected_param = 0;
248 mused.editpos = 0;
249 mused.mode = EDITINSTRUMENT;
250 mused.current_patternx = 0;
251 mused.current_sequencepos = 0;
252 mused.default_pattern_length = mused.sequenceview_steps = 64;
253 mused.current_sequencetrack = 0;
254 mused.time_signature = 0x0404;
255 mused.prev_mode = 0;
256 mused.edit_backup_buffer = NULL;
257 mused.mix_rate = 44100;
258 mused.mix_buffer = 2048;
259 mused.fx_room_size = 16;
260 mused.fx_room_vol = 16;
261 mused.fx_room_dec = 5;
262 mused.oversample = 2;
263
264 strcpy(mused.themename, "Default");
265 strcpy(mused.keymapname, "Default");
266
267 memset(&mused.cp, 0, sizeof(mused.cp));
268 memset(&mused.song, 0, sizeof(mused.song));
269 mused.song.instrument = instrument;
270 mused.song.pattern = pattern;
271 mused.channel = channel;
272
273 for (int i = 0 ; i < MUS_MAX_CHANNELS ; ++i)
274 {
275 mused.song.sequence[i] = sequence[i];
276 }
277
278 for (int i = 0 ; i < NUM_PATTERNS ; ++i)
279 {
280 mused.song.pattern[i].step = NULL;
281 mused.song.pattern[i].num_steps = 0;
282 mused.song.pattern[i].color = 0;
283 resize_pattern(&mused.song.pattern[i], mused.default_pattern_length);
284 }
285
286 undo_init(&mused.undo);
287 undo_init(&mused.redo);
288
289 enum_themes();
290 enum_keymaps();
291
292 //change_mode(EDITCLASSIC);
293 mused.mode = EDITCLASSIC;
294 mused.focus = EDITPATTERN;
295 mused.single_pattern_edit = 1;
296
297 debug("undo = %p redo = %p", mused.undo, mused.redo);
298
299 for (int i = 0 ; i < WG_CHAIN_OSCS ; ++i)
300 {
301 mused.wgset.chain[i].osc = WG_OSC_SINE;
302 mused.wgset.chain[i].mult = 1;
303 mused.wgset.chain[i].op = WG_OP_MUL;
304 mused.wgset.chain[i].shift = 0;
305 mused.wgset.chain[i].exp = 50;
306 mused.wgset.chain[i].flags = 0;
307 }
308
309 mused.wgset.num_oscs = 1;
310 mused.wgset.length = 256;
311
312 mused.prev_wavetable_x = -1;
313 mused.prev_wavetable_y = -1;
314
315 init_recent_files_list();
316
317 debug("init done");
318 }
319
320
init_scrollbars()321 void init_scrollbars()
322 {
323 slider_set_params(&mused.sequence_slider_param, 0, 0, 0, 0, &mused.sequence_position, 1, SLIDER_VERTICAL, mused.slider_bevel);
324 slider_set_params(&mused.pattern_slider_param, 0, 0, 0, 0, &mused.sequence_position, 1, SLIDER_VERTICAL, mused.slider_bevel);
325 slider_set_params(&mused.instrument_list_slider_param, 0, 0, 0, 0, &mused.sequence_position, 1, SLIDER_VERTICAL, mused.slider_bevel);
326 slider_set_params(&mused.pattern_horiz_slider_param, 0, 0, 0, 0, &mused.sequence_position, 1, SLIDER_HORIZONTAL, mused.slider_bevel);
327 slider_set_params(&mused.sequence_horiz_slider_param, 0, 0, 0, 0, &mused.sequence_position, 1, SLIDER_HORIZONTAL, mused.slider_bevel);
328 slider_set_params(&mused.program_slider_param, 0, 0, 0, 0, &mused.sequence_position, 1, SLIDER_VERTICAL, mused.slider_bevel);
329 slider_set_params(&mused.wavetable_list_slider_param, 0, 0, 0, 0, &mused.sequence_position, 1, SLIDER_VERTICAL, mused.slider_bevel);
330 }
331
332
deinit()333 void deinit()
334 {
335 deinit_recent_files_list();
336
337 undo_deinit(&mused.undo);
338 undo_deinit(&mused.redo);
339
340 console_destroy(mused.console);
341 if (mused.slider_bevel) gfx_free_surface(mused.slider_bevel);
342 if (mused.vu_meter) gfx_free_surface(mused.vu_meter);
343 if (mused.analyzer) gfx_free_surface(mused.analyzer);
344 if (mused.logo) gfx_free_surface(mused.logo);
345 if (mused.catometer) gfx_free_surface(mused.catometer);
346 if (mused.mouse_cursor_surface) gfx_free_surface(mused.mouse_cursor_surface);
347 if (mused.mouse_cursor) SDL_FreeCursor(mused.mouse_cursor);
348 if (mused.icon_surface) gfx_free_surface(mused.icon_surface);
349
350 font_destroy(&mused.smallfont);
351 font_destroy(&mused.largefont);
352 font_destroy(&mused.tinyfont);
353 font_destroy(&mused.tinyfont_sequence_counter);
354 font_destroy(&mused.tinyfont_sequence_normal);
355 font_destroy(&mused.menufont);
356 font_destroy(&mused.menufont_selected);
357 font_destroy(&mused.shortcutfont);
358 font_destroy(&mused.shortcutfont_selected);
359 font_destroy(&mused.headerfont);
360 font_destroy(&mused.headerfont_selected);
361 font_destroy(&mused.buttonfont);
362 free_themes();
363
364 if (mused.wavetable_preview)
365 gfx_free_surface(mused.wavetable_preview);
366 }
367
368
mirror_flags()369 void mirror_flags()
370 {
371 // We need to mirror the flags to the corresponding Cyd flags
372 for (int fx = 0 ; fx < CYD_MAX_FX_CHANNELS ; ++fx)
373 {
374 mused.cyd.fx[fx].flags = mused.song.fx[fx].flags;
375 }
376
377 mused.mus.volume = mused.song.master_volume;
378 }
379
380
viscol(int col)381 int viscol(int col)
382 {
383 const int tab[PED_PARAMS] = {
384 -1,
385 VC_INSTRUMENT,
386 VC_INSTRUMENT,
387 VC_VOLUME,
388 VC_VOLUME,
389 VC_CTRL,
390 VC_CTRL,
391 VC_CTRL,
392 VC_COMMAND,
393 VC_COMMAND,
394 VC_COMMAND,
395 VC_COMMAND
396 };
397 return !(mused.flags & COMPACT_VIEW) || (mused.visible_columns & tab[col]);
398 }
399
400
post_config_load()401 void post_config_load()
402 {
403 int new_val = mused.default_pattern_length;
404 mused.default_pattern_length = 16;
405
406 change_default_pattern_length(MAKEPTR(new_val), 0, 0);
407 change_visualizer(mused.current_visualizer);
408 }
409
410
tick_cb(void * data)411 static int tick_cb(void *data)
412 {
413 return mus_advance_tick(data);
414 }
415
416
enable_callback(bool state)417 void enable_callback(bool state)
418 {
419 if (state)
420 cyd_set_callback(&mused.cyd, tick_cb, &mused.mus, mused.song.song_rate);
421 else
422 cyd_set_callback(&mused.cyd, NULL, NULL, 0);
423 }
424
425
get_pattern(int abspos,int track)426 int get_pattern(int abspos, int track)
427 {
428 int p = -1;
429
430 const MusSeqPattern *sp = &mused.song.sequence[track][0];
431
432 for (int i = 0 ; i < mused.song.num_sequences[track] && sp->position <= abspos ; ++i, ++sp)
433 {
434 if (sp->position <= abspos && sp->position + mused.song.pattern[sp->pattern].num_steps > abspos) p = sp->pattern;
435 }
436
437 return p;
438 }
439
440
get_patternstep(int abspos,int track)441 int get_patternstep(int abspos, int track)
442 {
443 int p = -1;
444
445 const MusSeqPattern *sp = &mused.song.sequence[track][0];
446
447 for (int i = 0 ; i < mused.song.num_sequences[track] && sp->position <= abspos ; ++i, ++sp)
448 {
449 if (sp->position <= abspos && sp->position + mused.song.pattern[sp->pattern].num_steps > abspos) p = abspos - sp->position;
450 }
451
452 return p;
453 }
454
455
current_pattern()456 int current_pattern()
457 {
458 return current_pattern_for_channel(mused.current_sequencetrack);
459 }
460
461
current_pattern_for_channel(int channel)462 int current_pattern_for_channel(int channel)
463 {
464 int p = -1;
465
466 const MusSeqPattern *sp = &mused.song.sequence[channel][0];
467
468 for (int i = 0 ; i < mused.song.num_sequences[channel] && sp->position <= mused.current_patternpos ; ++i, ++sp)
469 {
470 if (sp->position <= mused.current_patternpos && sp->position + mused.song.pattern[sp->pattern].num_steps > mused.current_patternpos) p = sp->pattern;
471 }
472
473 return p;
474 }
475
476
current_patternstep()477 int current_patternstep()
478 {
479 int p = -1;
480
481 const MusSeqPattern *sp = &mused.song.sequence[mused.current_sequencetrack][0];
482
483 for (int i = 0 ; i < mused.song.num_sequences[mused.current_sequencetrack] && sp->position <= mused.current_patternpos ; ++i, ++sp)
484 {
485 if (sp->position <= mused.current_patternpos && sp->position + mused.song.pattern[sp->pattern].num_steps > mused.current_patternpos) p = mused.current_patternpos - sp->position;
486 }
487
488 return p;
489 }
490
491
get_current_step()492 MusStep * get_current_step()
493 {
494 MusPattern *pat = get_current_pattern();
495
496 if (!pat)
497 return NULL;
498
499 return &pat->step[current_patternstep()];
500 }
501
502
get_current_pattern()503 MusPattern * get_current_pattern()
504 {
505 int p = current_pattern();
506
507 if (p < 0)
508 return NULL;
509
510 return &mused.song.pattern[p];
511 }
512
513
change_visualizer(int vis)514 void change_visualizer(int vis)
515 {
516 mused.current_visualizer = vis;
517
518 for (int i = 0 ; analyzermenu[i].parent ; ++i)
519 analyzermenu[i].flags = (analyzermenu[i].flags & ~MENU_BULLET) | (mused.current_visualizer == CASTPTR(int, analyzermenu[i].p1) ? MENU_BULLET : 0);
520 }
521
522
info_message_cb(Uint32 interval,void * param)523 static Uint32 info_message_cb(Uint32 interval, void *param)
524 {
525 strcpy(mused.info_message, "");
526 SDL_RemoveTimer(mused.info_message_timer);
527 mused.info_message_timer = 0;
528 return 0;
529 }
530
set_info_message(const char * message,...)531 void set_info_message(const char *message, ...)
532 {
533 if (mused.info_message_timer)
534 SDL_RemoveTimer(mused.info_message_timer);
535
536 va_list args;
537 va_start(args, message);
538 vsnprintf(mused.info_message, sizeof(mused.info_message), message, args);
539 va_end(args);
540
541 mused.info_message_timer = SDL_AddTimer(5000, info_message_cb, NULL);
542 }
543
544
set_channels(int channels)545 void set_channels(int channels)
546 {
547 debug("Changed num_channels = %d", channels);
548 mused.song.num_channels = channels;
549 cyd_reserve_channels(&mused.cyd, channels);
550 }
551
552
get_playtime_at(int position)553 Uint32 get_playtime_at(int position)
554 {
555 return mus_get_playtime_at(&mused.song, position);
556 }
557