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