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 "edit.h"
27 #include "event.h"
28 #include "mused.h"
29 #include "gui/msgbox.h"
30 #include "view/wavetableview.h"
31 #include <string.h>
32 
33 extern Mused mused;
34 extern GfxDomain *domain;
35 
find_unused_pattern()36 static int find_unused_pattern()
37 {
38 	for (int empty = 0 ; empty < NUM_PATTERNS ; ++empty)
39 	{
40 		int not_empty = 0;
41 		for (int s = 0 ; s < mused.song.pattern[empty].num_steps && !not_empty ; ++s)
42 			if ((empty == current_pattern() && mused.focus == EDITPATTERN) || (mused.song.pattern[empty].step[s].note != MUS_NOTE_NONE
43 				|| mused.song.pattern[empty].step[s].ctrl != 0
44 				|| mused.song.pattern[empty].step[s].instrument != MUS_NOTE_NO_INSTRUMENT
45 				|| mused.song.pattern[empty].step[s].command != 0))
46 				not_empty = 1;
47 
48 		for (int c = 0 ; c < mused.song.num_channels && !not_empty ; ++c)
49 		{
50 			for (int i = 0 ; i < mused.song.num_sequences[c] && !not_empty ; ++i)
51 			{
52 				if (mused.song.sequence[c][i].pattern == empty)
53 					not_empty = 1;
54 			}
55 		}
56 
57 		if (!not_empty)
58 			return empty;
59 	}
60 
61 	msgbox(domain, mused.slider_bevel, &mused.largefont, "Max patterns exceeded!", MB_OK);
62 
63 	return -1;
64 }
65 
66 
set_pattern(int pattern)67 static void set_pattern(int pattern)
68 {
69 	//if ((mused.focus == EDITPATTERN && !mused.single_pattern_edit) || mused.focus != EDITPATTERN)
70 	{
71 		debug("setting pattern to %d at %d", pattern, mused.current_sequencepos);
72 
73 		snapshot(S_T_SEQUENCE);
74 
75 		for (int i = 0 ; i < mused.song.num_sequences[mused.current_sequencetrack] ; ++i)
76 		{
77 			if (mused.song.sequence[mused.current_sequencetrack][i].position == mused.current_sequencepos
78 				&& mused.song.sequence[mused.current_sequencetrack][i].pattern == current_pattern())
79 				mused.song.sequence[mused.current_sequencetrack][i].pattern = pattern;
80 		}
81 	}
82 }
83 
84 
clone_pattern(void * unused1,void * unused2,void * unused3)85 void clone_pattern(void *unused1, void *unused2, void *unused3)
86 {
87 	debug("Cloning pattern");
88 
89 	int empty = find_unused_pattern();
90 	int cp = current_pattern();
91 
92 	if (empty == -1 || cp == empty || cp < 0)
93 	{
94 		return;
95 	}
96 
97 	mused.song.pattern[empty].color = mused.song.pattern[cp].color;
98 
99 	resize_pattern(&mused.song.pattern[empty], mused.song.pattern[cp].num_steps);
100 
101 	memcpy(mused.song.pattern[empty].step, mused.song.pattern[cp].step,
102 		mused.song.pattern[cp].num_steps * sizeof(mused.song.pattern[cp].step[0]));
103 
104 	set_pattern(empty);
105 
106 	set_info_message("Cloned pattern %02X to %02X", cp, empty);
107 }
108 
109 
clone_each_pattern(void * unused1,void * unused2,void * unused3)110 void clone_each_pattern(void *unused1, void *unused2, void *unused3)
111 {
112 	debug("Cloning each pattern");
113 
114 	int temp = mused.current_sequencetrack;
115 
116 	for (int i = 0 ; i < mused.song.num_channels ; ++i)
117 	{
118 		mused.current_sequencetrack = i;
119 		clone_pattern(NULL, NULL, NULL);
120 	}
121 
122 	mused.current_sequencetrack = temp;
123 
124 	set_info_message("Cloned sequence row");
125 }
126 
127 
get_unused_pattern(void * unused1,void * unused2,void * unused3)128 void get_unused_pattern(void *unused1, void *unused2, void *unused3)
129 {
130 	int empty = find_unused_pattern();
131 
132 	if (empty == -1 || (current_pattern() == empty && mused.focus == EDITPATTERN))
133 	{
134 		return;
135 	}
136 
137 	if (mused.focus == EDITSEQUENCE)
138 	{
139 		snapshot(S_T_SEQUENCE);
140 
141 		bool found = false;
142 
143 		for (int i = 0 ; i < mused.song.num_sequences[mused.current_sequencetrack] ; ++i)
144 		{
145 			if (mused.song.sequence[mused.current_sequencetrack][i].position == mused.current_sequencepos
146 				&& mused.song.sequence[mused.current_sequencetrack][i].pattern == current_pattern())
147 				found = true;
148 		}
149 
150 		if (!found)
151 			add_sequence(mused.current_sequencetrack, mused.current_sequencepos, empty, 0);
152 	}
153 
154 	set_pattern(empty);
155 }
156 
157 
get_unused_pattern_all_tracks(void * unused1,void * unused2,void * unused3)158 void get_unused_pattern_all_tracks(void *unused1, void *unused2, void *unused3)
159 {
160 	int temp = mused.current_sequencetrack;
161 
162 	for (int i = 0 ; i < mused.song.num_channels ; ++i)
163 	{
164 		mused.current_sequencetrack = i;
165 		get_unused_pattern(0, 0, 0);
166 	}
167 
168 	mused.current_sequencetrack = temp;
169 }
170 
171 
zero_step(MusStep * step)172 void zero_step(MusStep *step)
173 {
174 	step->note = MUS_NOTE_NONE;
175 	step->instrument = MUS_NOTE_NO_INSTRUMENT;
176 	step->volume = MUS_NOTE_NO_VOLUME;
177 	step->ctrl = 0;
178 	step->command = 0;
179 }
180 
181 
expand_pattern(void * factor,void * unused2,void * unused3)182 void expand_pattern(void *factor, void *unused2, void *unused3)
183 {
184 	if (mused.focus != EDITPATTERN) return;
185 
186 	MusPattern *pattern = get_current_pattern();
187 
188 	if (!pattern)
189 		return;
190 
191 	MusStep *temp = malloc(pattern->num_steps * sizeof(pattern->step[0]));
192 	memcpy(temp, pattern->step, pattern->num_steps * sizeof(pattern->step[0]));
193 
194 	snapshot(S_T_PATTERN);
195 
196 	resize_pattern(pattern, pattern->num_steps * CASTPTR(int,factor));
197 
198 	for (int i = 0 ; i < pattern->num_steps ; ++i)
199 	{
200 		zero_step(&pattern->step[i]);
201 	}
202 
203 	for (int i = 0, ti = 0 ; i < pattern->num_steps ; ++i)
204 	{
205 		if ((i % CASTPTR(int,factor)) == 0)
206 		{
207 			memcpy(&pattern->step[i], &temp[ti], sizeof(pattern->step[i]));
208 			++ti;
209 		}
210 	}
211 
212 	free(temp);
213 }
214 
215 
shrink_pattern(void * factor,void * unused2,void * unused3)216 void shrink_pattern(void *factor, void *unused2, void *unused3)
217 {
218 	if (mused.focus != EDITPATTERN) return;
219 
220 	MusPattern *pattern = get_current_pattern();
221 
222 	if (!pattern)
223 		return;
224 
225 	if (pattern->num_steps <= CASTPTR(int,factor)) return;
226 
227 	snapshot(S_T_PATTERN);
228 
229 	resize_pattern(pattern, pattern->num_steps / CASTPTR(int,factor));
230 
231 	for (int i = 0, ti = 0 ; i < pattern->num_steps ; ++i, ti += CASTPTR(int,factor))
232 	{
233 		memcpy(&pattern->step[i], &pattern->step[ti], sizeof(pattern->step[i]));
234 	}
235 }
236 
237 
interpolate(void * unused1,void * unused2,void * unused3)238 void interpolate(void *unused1, void *unused2, void *unused3)
239 {
240 	if (mused.focus != EDITPATTERN || mused.selection.start >= mused.selection.end - 1) return;
241 
242 	MusPattern *pat = get_current_pattern();
243 
244 	if (!pat)
245 		return;
246 
247 	int start_step = get_patternstep(mused.selection.start, mused.current_sequencetrack);
248 
249 	Uint16 command = pat->step[start_step].command;
250 	Uint16 mask = 0xff00;
251 
252 	if ((command & 0xf000) == MUS_FX_CUTOFF_FINE_SET
253 		|| (command & 0xf000) == MUS_FX_WAVETABLE_OFFSET) mask = 0xf000;
254 
255 	command &= mask;
256 
257 	int begin = pat->step[start_step].command & ~mask;
258 	int end = pat->step[start_step + mused.selection.end - 1 - mused.selection.start].command & ~mask;
259 
260 	int l = mused.selection.end - mused.selection.start - 1;
261 
262 	snapshot(S_T_PATTERN);
263 
264 	for (int i = start_step, p = 0 ; p < mused.selection.end - mused.selection.start ; ++i, ++p)
265 	{
266 		if ((pat->step[i].command & mask) == command)
267 		{
268 			Uint16 val = begin + (end - begin) * p / l;
269 			pat->step[i].command = command | val;
270 		}
271 	}
272 }
273 
274 
snapshot(SHType type)275 void snapshot(SHType type)
276 {
277 	snapshot_cascade(type, -1, -1);
278 }
279 
280 
snapshot_cascade(SHType type,int a,int b)281 void snapshot_cascade(SHType type, int a, int b)
282 {
283 	if (!(a != -1 && mused.last_snapshot == type && mused.last_snapshot_a == a && mused.last_snapshot_b == b))
284 	{
285 		switch (type)
286 		{
287 			case S_T_PATTERN:
288 				undo_store_pattern(&mused.undo, current_pattern(), &mused.song.pattern[current_pattern()], mused.modified);
289 				break;
290 
291 			case S_T_SEQUENCE:
292 				undo_store_sequence(&mused.undo, mused.current_sequencetrack, mused.song.sequence[mused.current_sequencetrack], mused.song.num_sequences[mused.current_sequencetrack], mused.modified);
293 				break;
294 
295 			case S_T_MODE:
296 				undo_store_mode(&mused.undo, mused.mode, mused.focus, mused.modified);
297 				break;
298 
299 			case S_T_FX:
300 				undo_store_fx(&mused.undo, mused.fx_bus, &mused.song.fx[mused.fx_bus], mused.song.multiplex_period, mused.modified);
301 				break;
302 
303 			case S_T_SONGINFO:
304 				undo_store_songinfo(&mused.undo, &mused.song, mused.modified);
305 				break;
306 
307 			case S_T_INSTRUMENT:
308 				undo_store_instrument(&mused.undo, mused.current_instrument, &mused.song.instrument[mused.current_instrument], mused.modified);
309 				break;
310 
311 			case S_T_WAVE_PARAM:
312 				undo_store_wave_param(&mused.undo, mused.selected_wavetable, &mused.mus.cyd->wavetable_entries[mused.selected_wavetable], mused.modified);
313 				break;
314 
315 			case S_T_WAVE_DATA:
316 				undo_store_wave_data(&mused.undo, mused.selected_wavetable, &mused.mus.cyd->wavetable_entries[mused.selected_wavetable], mused.modified);
317 				break;
318 
319 			case S_T_WAVE_NAME:
320 				undo_store_wave_name(&mused.undo, mused.selected_wavetable, mused.song.wavetable_names[mused.selected_wavetable], mused.modified);
321 				break;
322 
323 			default: warning("SHType %d not implemented", type); break;
324 		}
325 	}
326 
327 	mused.last_snapshot = type;
328 	mused.last_snapshot_a = a;
329 	mused.last_snapshot_b = b;
330 
331 	if (type != S_T_MODE)
332 		mused.modified = true;
333 }
334 
335 
transpose_note_data(void * semitones,void * unused1,void * unused2)336 void transpose_note_data(void *semitones, void *unused1, void *unused2)
337 {
338 	if (mused.focus != EDITPATTERN || mused.selection.start >= mused.selection.end)
339     return;
340 
341 	MusPattern *pat = get_current_pattern();
342 
343 	if (!pat)
344 		return;
345 
346 	snapshot(S_T_PATTERN);
347 
348 	debug("Transposing pattern %d (%d-%d)", current_pattern(), mused.selection.start, mused.selection.end);
349 
350 	for (int i = get_patternstep(mused.selection.start, mused.current_sequencetrack), p = 0 ; p < mused.selection.end - mused.selection.start ; ++i, ++p)
351 	{
352 		if (pat->step[i].note != MUS_NOTE_NONE)
353 		{
354 			int semi = CASTPTR(int,semitones);
355 			int note = (int)pat->step[i].note + semi;
356 
357 			if (note >= 0 && note < 12 * 8)
358 				pat->step[i].note = note;
359 		}
360 	}
361 }
362 
363 
split_pattern(void * unused1,void * unused2,void * unused3)364 void split_pattern(void *unused1, void *unused2, void *unused3)
365 {
366 	if (mused.focus != EDITPATTERN && mused.focus != EDITSEQUENCE) return;
367 
368 	MusPattern *pat = get_current_pattern();
369 
370 	if (!pat)
371 		return;
372 
373 	int step = current_patternstep();
374 
375 	if (step <= 0)
376 		return;
377 
378 	int empty = find_unused_pattern();
379 
380 	if (empty == -1 || (current_pattern() == empty && mused.focus == EDITPATTERN))
381 	{
382 		return;
383 	}
384 
385 	int cursor_pos;
386 
387 	if (mused.focus == EDITSEQUENCE)
388 		cursor_pos = mused.current_sequencepos;
389 	else
390 		cursor_pos = mused.current_patternpos;
391 
392 	int cp = current_pattern();
393 	MusPattern *new_pattern = &mused.song.pattern[empty];
394 
395 	// Add new pattern in sequence
396 
397 	snapshot(S_T_SEQUENCE);
398 	add_sequence(mused.current_sequencetrack, cursor_pos, empty, 0);
399 
400 	// Copy latter half to the new pattern
401 
402 	snapshot(S_T_PATTERN);
403 	resize_pattern(new_pattern, pat->num_steps - step);
404 	memcpy(new_pattern->step, &pat->step[step], sizeof(pat->step[0]) * ((int)pat->num_steps - step));
405 
406 	// Resize old pattern
407 
408 	snapshot(S_T_PATTERN);
409 	resize_pattern(pat, step);
410 
411 	set_info_message("Split %02X into %02X and %02X", cp, cp, empty);
412 }
413