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