1 /*
2 * Schism Tracker - a cross-platform Impulse Tracker clone
3 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5 * copyright (c) 2009 Storlek & Mrs. Brisby
6 * copyright (c) 2010-2012 Storlek
7 * URL: http://schismtracker.org/
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "headers.h"
25
26 #include "it.h"
27 #include "song.h"
28 #include "sndfile.h"
29 #include "slurp.h"
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <math.h>
34
35 // ------------------------------------------------------------------------
36 // variables
37
38 song_t *current_song = NULL;
39
40 // ------------------------------------------------------------------------
41 // song information
42
song_get_length_to(int order,int row)43 unsigned int song_get_length_to(int order, int row)
44 {
45 unsigned int t;
46
47 song_lock_audio();
48 current_song->stop_at_order = order;
49 current_song->stop_at_row = row;
50 t = csf_get_length(current_song);
51 current_song->stop_at_order = current_song->stop_at_row = -1;
52 song_unlock_audio();
53 return t;
54 }
song_get_at_time(unsigned int seconds,int * order,int * row)55 void song_get_at_time(unsigned int seconds, int *order, int *row)
56 {
57 if (!seconds) {
58 if (order) *order = 0;
59 if (row) *row = 0;
60 } else {
61 song_lock_audio();
62 current_song->stop_at_order = MAX_ORDERS;
63 current_song->stop_at_row = 255; /* unpossible */
64 current_song->stop_at_time = seconds;
65 csf_get_length(current_song);
66 if (order) *order = current_song->stop_at_order;
67 if (row) *row = current_song->stop_at_row;
68 current_song->stop_at_order = current_song->stop_at_row = -1;
69 current_song->stop_at_time = 0;
70 song_unlock_audio();
71 }
72 }
73
song_get_sample(int n)74 song_sample_t *song_get_sample(int n)
75 {
76 if (n >= MAX_SAMPLES)
77 return NULL;
78 return current_song->samples + n;
79 }
80
song_get_instrument(int n)81 song_instrument_t *song_get_instrument(int n)
82 {
83 if (n >= MAX_INSTRUMENTS)
84 return NULL;
85
86 // Make a new instrument if it doesn't exist.
87 if (!current_song->instruments[n]) {
88 current_song->instruments[n] = csf_allocate_instrument();
89 }
90
91 return (song_instrument_t *) current_song->instruments[n];
92 }
93
94 // this is a fairly gross way to do what should be such a simple thing
song_get_instrument_number(song_instrument_t * inst)95 int song_get_instrument_number(song_instrument_t *inst)
96 {
97 if (inst)
98 for (int n = 1; n < MAX_INSTRUMENTS; n++)
99 if (inst == ((song_instrument_t *) current_song->instruments[n]))
100 return n;
101 return 0;
102 }
103
song_get_channel(int n)104 song_channel_t *song_get_channel(int n)
105 {
106 if (n >= MAX_CHANNELS)
107 return NULL;
108 return (song_channel_t *) current_song->channels + n;
109 }
110
song_get_mix_channel(int n)111 song_voice_t *song_get_mix_channel(int n)
112 {
113 if (n >= MAX_VOICES)
114 return NULL;
115 return (song_voice_t *) current_song->voices + n;
116 }
117
song_get_mix_state(unsigned int ** channel_list)118 int song_get_mix_state(unsigned int **channel_list)
119 {
120 if (channel_list)
121 *channel_list = current_song->voice_mix;
122 return MIN(current_song->num_voices, max_voices);
123 }
124
125 // ------------------------------------------------------------------------
126 // For all of these, channel is ZERO BASED.
127 // (whereas in the pattern editor etc. it's one based)
128
129 static int channel_states[64]; // saved ("real") mute settings; nonzero = muted
130
_save_state(int channel)131 static inline void _save_state(int channel)
132 {
133 channel_states[channel] = current_song->voices[channel].flags & CHN_MUTE;
134 }
135
song_save_channel_states(void)136 void song_save_channel_states(void)
137 {
138 int n = 64;
139
140 while (n-- > 0)
141 _save_state(n);
142 }
_fix_mutes_like(int chan)143 static inline void _fix_mutes_like(int chan)
144 {
145 int i;
146 for (i = 0; i < MAX_VOICES; i++) {
147 if (i == chan) continue;
148 if (((int)current_song->voices[i].master_channel) != (chan+1)) continue;
149 current_song->voices[i].flags = (current_song->voices[i].flags & (~(CHN_MUTE)))
150 | (current_song->voices[chan].flags & (CHN_MUTE));
151 }
152 }
153
song_set_channel_mute(int channel,int muted)154 void song_set_channel_mute(int channel, int muted)
155 {
156 if (muted) {
157 current_song->channels[channel].flags |= CHN_MUTE;
158 current_song->voices[channel].flags |= CHN_MUTE;
159 } else {
160 current_song->channels[channel].flags &= ~CHN_MUTE;
161 current_song->voices[channel].flags &= ~CHN_MUTE;
162 _save_state(channel);
163 }
164 _fix_mutes_like(channel);
165 }
166
167 // I don't think this is useful besides undoing a channel solo (a few lines
168 // below), but I'm making it extern anyway for symmetry.
song_restore_channel_states(void)169 inline void song_restore_channel_states(void)
170 {
171 int n = 64;
172
173 while (n-- > 0)
174 song_set_channel_mute(n, channel_states[n]);
175 }
176
song_toggle_channel_mute(int channel)177 void song_toggle_channel_mute(int channel)
178 {
179 // i'm just going by the playing channel's state...
180 // if the actual channel is muted but not the playing one,
181 // tough luck :)
182 song_set_channel_mute(channel, (current_song->voices[channel].flags & CHN_MUTE) == 0);
183 }
184
_soloed(int channel)185 static int _soloed(int channel) {
186 int n = 64;
187 // if this channel is muted, it obviously isn't soloed
188 if (current_song->voices[channel].flags & CHN_MUTE)
189 return 0;
190 while (n-- > 0) {
191 if (n == channel)
192 continue;
193 if (!(current_song->voices[n].flags & CHN_MUTE))
194 return 0;
195 }
196 return 1;
197 }
198
song_handle_channel_solo(int channel)199 void song_handle_channel_solo(int channel)
200 {
201 int n = 64;
202
203 if (_soloed(channel)) {
204 song_restore_channel_states();
205 } else {
206 while (n-- > 0)
207 song_set_channel_mute(n, n != channel);
208 }
209 }
210
211 // returned channel number is ONE-based
212 // (to make it easier to work with in the pattern editor and info page)
song_find_last_channel(void)213 int song_find_last_channel(void)
214 {
215 int n = 64;
216
217 while (channel_states[--n])
218 if (n == 0)
219 return 64;
220 return n + 1;
221 }
222
223 // ------------------------------------------------------------------------
224
225 // returns length of the pattern, or 0 on error. (this can be used to
226 // get a pattern's length by passing NULL for buf.)
song_get_pattern(int n,song_note_t ** buf)227 int song_get_pattern(int n, song_note_t ** buf)
228 {
229 if (n >= MAX_PATTERNS)
230 return 0;
231
232 if (buf) {
233 if (!current_song->patterns[n]) {
234 current_song->pattern_size[n] = 64;
235 current_song->pattern_alloc_size[n] = 64;
236 current_song->patterns[n] = csf_allocate_pattern(current_song->pattern_size[n]);
237 }
238 *buf = current_song->patterns[n];
239 } else {
240 if (!current_song->patterns[n])
241 return 64;
242 }
243 return current_song->pattern_size[n];
244 }
song_pattern_allocate_copy(int patno,int * rows)245 song_note_t *song_pattern_allocate_copy(int patno, int *rows)
246 {
247 int len = current_song->pattern_size[patno];
248 song_note_t *olddata = current_song->patterns[patno];
249 song_note_t *newdata = NULL;
250 if (olddata) {
251 newdata = csf_allocate_pattern(len);
252 memcpy(newdata, olddata, len * sizeof(song_note_t) * 64);
253 }
254 if (rows)
255 *rows = len;
256 return newdata;
257 }
song_pattern_install(int patno,song_note_t * n,int rows)258 void song_pattern_install(int patno, song_note_t *n, int rows)
259 {
260 song_lock_audio();
261
262 song_note_t *olddata = current_song->patterns[patno];
263 csf_free_pattern(olddata);
264
265 current_song->patterns[patno] = n;
266 current_song->pattern_alloc_size[patno] = rows;
267 current_song->pattern_size[patno] = rows;
268
269 song_unlock_audio();
270 }
271
272 // ------------------------------------------------------------------------
273
song_next_order_for_pattern(int pat)274 int song_next_order_for_pattern(int pat)
275 {
276 int i, ord = current_song->current_order;
277
278 ord = CLAMP(ord, 0, 255);
279
280 for (i = ord; i < 255; i++) {
281 if (current_song->orderlist[i] == pat) {
282 return i;
283 }
284 }
285 for (i = 0; i < ord; i++) {
286 if (current_song->orderlist[i] == pat) {
287 return i;
288 }
289 }
290 return -1;
291 }
292
293
song_get_rows_in_pattern(int pattern)294 int song_get_rows_in_pattern(int pattern)
295 {
296 if (pattern > MAX_PATTERNS)
297 return 0;
298 return (current_song->pattern_size[pattern] ? : 64) - 1;
299 }
300
301 // ------------------------------------------------------------------------
302
song_pattern_resize(int pattern,int newsize)303 void song_pattern_resize(int pattern, int newsize)
304 {
305 song_lock_audio();
306
307 int oldsize = current_song->pattern_alloc_size[pattern];
308 status.flags |= SONG_NEEDS_SAVE;
309
310 if (!current_song->patterns[pattern] && newsize != 64) {
311 current_song->patterns[pattern] = csf_allocate_pattern(newsize);
312 current_song->pattern_alloc_size[pattern] = newsize;
313
314 } else if (oldsize < newsize) {
315 song_note_t *olddata = current_song->patterns[pattern];
316 song_note_t *newdata = csf_allocate_pattern(newsize);
317 if (olddata) {
318 memcpy(newdata, olddata, 64 * sizeof(song_note_t) * MIN(newsize, oldsize));
319 csf_free_pattern(olddata);
320 }
321 current_song->patterns[pattern] = newdata;
322 current_song->pattern_alloc_size[pattern] = MAX(newsize,oldsize);
323 }
324 current_song->pattern_size[pattern] = newsize;
325 song_unlock_audio();
326 }
327
328 // ------------------------------------------------------------------------
329
song_set_initial_speed(int new_speed)330 void song_set_initial_speed(int new_speed)
331 {
332 current_song->initial_speed = CLAMP(new_speed, 1, 255);
333 }
334
song_set_initial_tempo(int new_tempo)335 void song_set_initial_tempo(int new_tempo)
336 {
337 current_song->initial_tempo = CLAMP(new_tempo, 31, 255);
338 }
339
song_set_initial_global_volume(int new_vol)340 void song_set_initial_global_volume(int new_vol)
341 {
342 current_song->initial_global_volume = CLAMP(new_vol, 0, 128);
343 }
344
song_set_mixing_volume(int new_vol)345 void song_set_mixing_volume(int new_vol)
346 {
347 current_song->mixing_volume = CLAMP(new_vol, 0, 128);
348 }
349
song_set_separation(int new_sep)350 void song_set_separation(int new_sep)
351 {
352 current_song->pan_separation = CLAMP(new_sep, 0, 128);
353 }
354
song_is_stereo(void)355 int song_is_stereo(void)
356 {
357 if (current_song->flags & SONG_NOSTEREO) return 0;
358 return 1;
359 }
song_toggle_stereo(void)360 void song_toggle_stereo(void)
361 {
362 current_song->flags ^= SONG_NOSTEREO;
363 song_vars_sync_stereo();
364 }
song_toggle_mono(void)365 void song_toggle_mono(void)
366 {
367 current_song->flags ^= SONG_NOSTEREO;
368 song_vars_sync_stereo();
369 }
song_set_mono(void)370 void song_set_mono(void)
371 {
372 current_song->flags |= SONG_NOSTEREO;
373 song_vars_sync_stereo();
374 }
song_set_stereo(void)375 void song_set_stereo(void)
376 {
377 current_song->flags &= ~SONG_NOSTEREO;
378 song_vars_sync_stereo();
379 }
380
song_has_old_effects(void)381 int song_has_old_effects(void)
382 {
383 return !!(current_song->flags & SONG_ITOLDEFFECTS);
384 }
385
song_set_old_effects(int value)386 void song_set_old_effects(int value)
387 {
388 if (value)
389 current_song->flags |= SONG_ITOLDEFFECTS;
390 else
391 current_song->flags &= ~SONG_ITOLDEFFECTS;
392 }
393
song_has_compatible_gxx(void)394 int song_has_compatible_gxx(void)
395 {
396 return !!(current_song->flags & SONG_COMPATGXX);
397 }
398
song_set_compatible_gxx(int value)399 void song_set_compatible_gxx(int value)
400 {
401 if (value)
402 current_song->flags |= SONG_COMPATGXX;
403 else
404 current_song->flags &= ~SONG_COMPATGXX;
405 }
406
song_has_linear_pitch_slides(void)407 int song_has_linear_pitch_slides(void)
408 {
409 return !!(current_song->flags & SONG_LINEARSLIDES);
410 }
411
song_set_linear_pitch_slides(int value)412 void song_set_linear_pitch_slides(int value)
413 {
414 if (value)
415 current_song->flags |= SONG_LINEARSLIDES;
416 else
417 current_song->flags &= ~SONG_LINEARSLIDES;
418 }
419
song_is_instrument_mode(void)420 int song_is_instrument_mode(void)
421 {
422 return !!(current_song->flags & SONG_INSTRUMENTMODE);
423 }
424
song_set_instrument_mode(int value)425 void song_set_instrument_mode(int value)
426 {
427 int oldvalue = song_is_instrument_mode();
428 int i, j;
429
430 if (value && !oldvalue) {
431 current_song->flags |= SONG_INSTRUMENTMODE;
432 for (i = 0; i < MAX_INSTRUMENTS; i++) {
433 if (!current_song->instruments[i]) continue;
434 /* fix wiped notes */
435 for (j = 0; j < 128; j++) {
436 if (current_song->instruments[i]->note_map[j] < 1
437 || current_song->instruments[i]->note_map[j] > 120)
438 current_song->instruments[i]->note_map[j] = j+1;
439 }
440 }
441 } else if (!value && oldvalue) {
442 current_song->flags &= ~SONG_INSTRUMENTMODE;
443 }
444 }
445
song_get_current_instrument(void)446 int song_get_current_instrument(void)
447 {
448 return (song_is_instrument_mode() ? instrument_get_current() : sample_get_current());
449 }
450
451 // ------------------------------------------------------------------------
452
song_exchange_samples(int a,int b)453 void song_exchange_samples(int a, int b)
454 {
455 if (a == b)
456 return;
457
458 song_lock_audio();
459 song_sample_t tmp;
460 memcpy(&tmp, current_song->samples + a, sizeof(song_sample_t));
461 memcpy(current_song->samples + a, current_song->samples + b, sizeof(song_sample_t));
462 memcpy(current_song->samples + b, &tmp, sizeof(song_sample_t));
463 status.flags |= SONG_NEEDS_SAVE;
464 song_unlock_audio();
465 }
466
song_copy_instrument(int dst,int src)467 void song_copy_instrument(int dst, int src)
468 {
469 if (src == dst) return;
470
471 song_lock_audio();
472 song_get_instrument(dst);
473 song_get_instrument(src);
474 *(current_song->instruments[dst]) = *(current_song->instruments[src]);
475 status.flags |= SONG_NEEDS_SAVE;
476 song_unlock_audio();
477 }
478
song_exchange_instruments(int a,int b)479 void song_exchange_instruments(int a, int b)
480 {
481 if (a == b)
482 return;
483
484 song_instrument_t *tmp;
485
486 song_lock_audio();
487 tmp = current_song->instruments[a];
488 current_song->instruments[a] = current_song->instruments[b];
489 current_song->instruments[b] = tmp;
490 status.flags |= SONG_NEEDS_SAVE;
491 song_unlock_audio();
492 }
493
494 // instrument, sample, whatever.
_swap_instruments_in_patterns(int a,int b)495 static void _swap_instruments_in_patterns(int a, int b)
496 {
497 for (int pat = 0; pat < MAX_PATTERNS; pat++) {
498 song_note_t *note = current_song->patterns[pat];
499 if (note == NULL)
500 continue;
501 for (int n = 0; n < 64 * current_song->pattern_size[pat]; n++, note++) {
502 if (note->instrument == a)
503 note->instrument = b;
504 else if (note->instrument == b)
505 note->instrument = a;
506 }
507 }
508 }
509
song_swap_samples(int a,int b)510 void song_swap_samples(int a, int b)
511 {
512 if (a == b)
513 return;
514
515 song_lock_audio();
516 if (song_is_instrument_mode()) {
517 // ... or should this be done even in sample mode?
518 for (int n = 1; n < MAX_INSTRUMENTS; n++) {
519 song_instrument_t *ins = current_song->instruments[n];
520
521 if (ins == NULL)
522 continue;
523 // sizeof(ins->sample_map)...
524 for (int s = 0; s < 128; s++) {
525 if (ins->sample_map[s] == (unsigned int)a)
526 ins->sample_map[s] = (unsigned int)b;
527 else if (ins->sample_map[s] == (unsigned int)b)
528 ins->sample_map[s] = (unsigned int)a;
529 }
530 }
531 } else {
532 _swap_instruments_in_patterns(a, b);
533 }
534 song_unlock_audio();
535 song_exchange_samples(a, b);
536 }
537
song_swap_instruments(int a,int b)538 void song_swap_instruments(int a, int b)
539 {
540 if (a == b)
541 return;
542
543 if (song_is_instrument_mode()) {
544 song_lock_audio();
545 _swap_instruments_in_patterns(a, b);
546 song_unlock_audio();
547 }
548 song_exchange_instruments(a, b);
549 }
550
_adjust_instruments_in_patterns(int start,int delta)551 static void _adjust_instruments_in_patterns(int start, int delta)
552 {
553 int pat, n;
554
555 for (pat = 0; pat < MAX_PATTERNS; pat++) {
556 song_note_t *note = current_song->patterns[pat];
557 if (note == NULL)
558 continue;
559 for (n = 0; n < 64 * current_song->pattern_size[pat]; n++, note++) {
560 if (note->instrument >= start)
561 note->instrument = CLAMP(note->instrument + delta, 0, MAX_SAMPLES - 1);
562 }
563 }
564 }
565
_adjust_samples_in_instruments(int start,int delta)566 static void _adjust_samples_in_instruments(int start, int delta)
567 {
568 int n, s;
569
570 for (n = 1; n < MAX_INSTRUMENTS; n++) {
571 song_instrument_t *ins = current_song->instruments[n];
572
573 if (ins == NULL)
574 continue;
575 // sizeof...
576 for (s = 0; s < 128; s++) {
577 if (ins->sample_map[s] >= (unsigned int) start) {
578 ins->sample_map[s] = (unsigned int) CLAMP(
579 ((int) ins->sample_map[s]) + delta,
580 0, MAX_SAMPLES - 1);
581 }
582 }
583 }
584 }
585
song_init_instrument_from_sample(int insn,int samp)586 void song_init_instrument_from_sample(int insn, int samp)
587 {
588 if (!csf_instrument_is_empty(current_song->instruments[insn])) return;
589 if (current_song->samples[samp].data == NULL) return;
590 song_get_instrument(insn);
591 song_instrument_t *ins = current_song->instruments[insn];
592 if (!ins) return; /* eh? */
593
594 csf_init_instrument(ins, samp);
595
596 // IT doesn't set instrument filenames unless loading an instrument from disk
597 //memcpy(ins->filename, current_song->samples[samp].filename, 12);
598 memcpy(ins->name, current_song->samples[samp].name, 32);
599 }
600
song_init_instruments(int qq)601 void song_init_instruments(int qq)
602 {
603 for (int n = 1; n < MAX_INSTRUMENTS; n++) {
604 if (qq > -1 && qq != n) continue;
605 song_init_instrument_from_sample(n,n);
606 }
607 }
608
song_insert_sample_slot(int n)609 void song_insert_sample_slot(int n)
610 {
611 if (current_song->samples[MAX_SAMPLES - 1].data != NULL)
612 return;
613
614 status.flags |= SONG_NEEDS_SAVE;
615 song_lock_audio();
616
617 memmove(current_song->samples + n + 1, current_song->samples + n, (MAX_SAMPLES - n - 1) * sizeof(song_sample_t));
618 memset(current_song->samples + n, 0, sizeof(song_sample_t));
619 current_song->samples[n].c5speed = 8363;
620 current_song->samples[n].volume = 64 * 4;
621 current_song->samples[n].global_volume = 64;
622 if (song_is_instrument_mode())
623 _adjust_samples_in_instruments(n, 1);
624 else
625 _adjust_instruments_in_patterns(n, 1);
626
627 song_unlock_audio();
628 }
629
song_remove_sample_slot(int n)630 void song_remove_sample_slot(int n)
631 {
632 if (current_song->samples[n].data != NULL)
633 return;
634
635 song_lock_audio();
636
637 status.flags |= SONG_NEEDS_SAVE;
638 memmove(current_song->samples + n, current_song->samples + n + 1, (MAX_SAMPLES - n - 1) * sizeof(song_sample_t));
639 memset(current_song->samples + MAX_SAMPLES - 1, 0, sizeof(song_sample_t));
640 current_song->samples[MAX_SAMPLES - 1].c5speed = 8363;
641 current_song->samples[MAX_SAMPLES - 1].volume = 64 * 4;
642 current_song->samples[MAX_SAMPLES - 1].global_volume = 64;
643
644 if (song_is_instrument_mode())
645 _adjust_samples_in_instruments(n, -1);
646 else
647 _adjust_instruments_in_patterns(n, -1);
648
649 song_unlock_audio();
650 }
651
song_insert_instrument_slot(int n)652 void song_insert_instrument_slot(int n)
653 {
654 int i;
655
656 if (!csf_instrument_is_empty(current_song->instruments[MAX_INSTRUMENTS - 1]))
657 return;
658
659 status.flags |= SONG_NEEDS_SAVE;
660 song_lock_audio();
661 for (i = MAX_INSTRUMENTS - 1; i > n; i--)
662 current_song->instruments[i] = current_song->instruments[i-1];
663 current_song->instruments[n] = NULL;
664 _adjust_instruments_in_patterns(n, 1);
665 song_unlock_audio();
666 }
667
song_remove_instrument_slot(int n)668 void song_remove_instrument_slot(int n)
669 {
670 int i;
671
672 if (!csf_instrument_is_empty(current_song->instruments[n]))
673 return;
674
675 song_lock_audio();
676 for (i = n; i < MAX_INSTRUMENTS; i++)
677 current_song->instruments[i] = current_song->instruments[i+1];
678 current_song->instruments[MAX_INSTRUMENTS - 1] = NULL;
679 _adjust_instruments_in_patterns(n, -1);
680 song_unlock_audio();
681 }
682
song_wipe_instrument(int n)683 void song_wipe_instrument(int n)
684 {
685 /* wee .... */
686 if (csf_instrument_is_empty(current_song->instruments[n]))
687 return;
688 if (!current_song->instruments[n])
689 return;
690
691 status.flags |= SONG_NEEDS_SAVE;
692 song_lock_audio();
693 csf_free_instrument(current_song->instruments[n]);
694 current_song->instruments[n] = NULL;
695 song_unlock_audio();
696 }
697
698 // Returns 1 if sample `n` is used by the specified instrument; 0 otherwise.
_song_sample_used_by_instrument(int n,const song_instrument_t * instrument)699 static int _song_sample_used_by_instrument(int n, const song_instrument_t *instrument)
700 {
701 int i;
702
703 for (i = 0; i < sizeof instrument->sample_map; i++) {
704 if (instrument->sample_map[i] == n) {
705 return 1;
706 }
707 }
708
709 return 0;
710 }
711
712 // Returns 1 if sample `n` is used by at least two instruments; 0 otherwise.
_song_sample_used_by_many_instruments(int n)713 static int _song_sample_used_by_many_instruments(int n)
714 {
715 const song_instrument_t *instrument;
716 int found;
717 int i;
718
719 found = 0;
720
721 for (i = 1; i < MAX_INSTRUMENTS+1; i++) {
722 instrument = current_song->instruments[i];
723 if (instrument != NULL) {
724 if (_song_sample_used_by_instrument(n, instrument)) {
725 if (found) {
726 return 1;
727 }
728 found = 1;
729 }
730 }
731 }
732
733 return 0;
734 }
735
736 // n: The index of the instrument to delete (base-1).
737 // preserve_samples: If 0, delete all samples used by instrument.
738 // If 1, only delete samples that no other instruments use.
song_delete_instrument(int n,int preserve_samples)739 void song_delete_instrument(int n, int preserve_samples)
740 {
741 unsigned long i;
742 int j;
743
744 if (!current_song->instruments[n])
745 return;
746 // 128? really?
747 for (i = 0; i < 128; i++) {
748 j = current_song->instruments[n]->sample_map[i];
749 if (j) {
750 if (!preserve_samples || !_song_sample_used_by_many_instruments(j)) {
751 song_clear_sample(j);
752 }
753 }
754 }
755 song_wipe_instrument(n);
756 }
757
song_replace_sample(int num,int with)758 void song_replace_sample(int num, int with)
759 {
760 int i, j;
761 song_instrument_t *ins;
762 song_note_t *note;
763
764 if (num < 1 || num > MAX_SAMPLES
765 || with < 1 || with > MAX_SAMPLES)
766 return;
767
768 if (song_is_instrument_mode()) {
769 // for each instrument, for each note in the keyboard table, replace 'smp' with 'with'
770
771 for (i = 1; i < MAX_INSTRUMENTS; i++) {
772 ins = current_song->instruments[i];
773 if (!ins)
774 continue;
775 for (j = 0; j < 128; j++) {
776 if ((int) ins->sample_map[j] == num)
777 ins->sample_map[j] = with;
778 }
779 }
780 } else {
781 // for each pattern, for each note, replace 'smp' with 'with'
782 for (i = 0; i < MAX_PATTERNS; i++) {
783 note = current_song->patterns[i];
784 if (!note)
785 continue;
786 for (j = 0; j < 64 * current_song->pattern_size[i]; j++, note++) {
787 if (note->instrument == num)
788 note->instrument = with;
789 }
790 }
791 }
792 }
793
song_replace_instrument(int num,int with)794 void song_replace_instrument(int num, int with)
795 {
796 int i, j;
797 song_note_t *note;
798
799 if (num < 1 || num > MAX_INSTRUMENTS
800 || with < 1 || with > MAX_INSTRUMENTS
801 || !song_is_instrument_mode())
802 return;
803
804 // for each pattern, for each note, replace 'ins' with 'with'
805 for (i = 0; i < MAX_PATTERNS; i++) {
806 note = current_song->patterns[i];
807 if (!note)
808 continue;
809 for (j = 0; j < 64 * current_song->pattern_size[i]; j++, note++) {
810 if (note->instrument == num)
811 note->instrument = with;
812 }
813 }
814 }
815
816