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