958 lines
18 KiB
C++
958 lines
18 KiB
C++
/*************************************************************************/
|
|
/* cp_song.cpp */
|
|
/*************************************************************************/
|
|
/* This file is part of: */
|
|
/* GODOT ENGINE */
|
|
/* http://www.godotengine.org */
|
|
/*************************************************************************/
|
|
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
|
|
/* */
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
/* a copy of this software and associated documentation files (the */
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
/* the following conditions: */
|
|
/* */
|
|
/* The above copyright notice and this permission notice shall be */
|
|
/* included in all copies or substantial portions of the Software. */
|
|
/* */
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
/*************************************************************************/
|
|
#include "cp_song.h"
|
|
|
|
void CPSong::set_name(const char *p_name) {
|
|
|
|
if (p_name==NULL) {
|
|
variables.name[0]=0;
|
|
return;
|
|
}
|
|
|
|
|
|
bool done=false;
|
|
for (int i=0;i<MAX_SONG_NAME;i++) {
|
|
|
|
|
|
variables.name[i]=done?0:p_name[i];
|
|
if (!done && p_name[i]==0)
|
|
done=true;
|
|
}
|
|
|
|
variables.name[MAX_SONG_NAME-1]=0; /* just in case */
|
|
}
|
|
|
|
const char * CPSong::get_name() {
|
|
|
|
return variables.name;
|
|
|
|
}
|
|
|
|
void CPSong::set_message(const char *p_message) {
|
|
|
|
if (p_message==NULL) {
|
|
variables.message[0]=0;
|
|
return;
|
|
}
|
|
|
|
bool done=false;
|
|
for (int i=0;i<MAX_MESSAGE_LEN;i++) {
|
|
|
|
|
|
variables.message[i]=done?0:p_message[i];
|
|
if (!done && p_message[i]==0)
|
|
done=true;
|
|
}
|
|
|
|
variables.message[MAX_MESSAGE_LEN-1]=0; /* just in case */
|
|
}
|
|
|
|
const char * CPSong::get_message() {
|
|
|
|
return variables.message;
|
|
|
|
}
|
|
|
|
void CPSong::set_row_highlight_minor(int p_hl_minor) {
|
|
|
|
variables.row_highlight_minor=p_hl_minor;
|
|
}
|
|
int CPSong::get_row_highlight_minor() {
|
|
|
|
return variables.row_highlight_minor;
|
|
}
|
|
|
|
void CPSong::set_row_highlight_major(int p_hl_major) {
|
|
|
|
variables.row_highlight_major=p_hl_major;
|
|
|
|
|
|
} /* 0 .. 256 */
|
|
int CPSong::get_row_highlight_major() {
|
|
|
|
return variables.row_highlight_major;
|
|
|
|
|
|
} /* 0 .. 256 */
|
|
|
|
void CPSong::set_mixing_volume(int p_mix_volume) {
|
|
|
|
|
|
variables.mixing_volume=p_mix_volume;
|
|
} /* 0 .. 128 */
|
|
int CPSong::get_mixing_volume() {
|
|
|
|
return variables.mixing_volume;
|
|
|
|
} /* 0 .. 128 */
|
|
|
|
void CPSong::set_global_volume(int p_global_volume) {
|
|
|
|
|
|
initial_variables.global_volume=p_global_volume;
|
|
|
|
} /* 0 .. 128 */
|
|
int CPSong::get_global_volume() {
|
|
|
|
return initial_variables.global_volume;
|
|
|
|
} /* 0 .. 128 */
|
|
|
|
void CPSong::set_stereo_separation(int p_separation) {
|
|
|
|
variables.stereo_separation=p_separation;
|
|
|
|
} /* 0 .. 128 */
|
|
int CPSong::get_stereo_separation() {
|
|
|
|
return variables.stereo_separation;
|
|
} /* 0 .. 128 */
|
|
|
|
void CPSong::set_stereo(bool p_stereo) {
|
|
|
|
variables.use_stereo=p_stereo;
|
|
|
|
}
|
|
bool CPSong::is_stereo() {
|
|
|
|
return variables.use_stereo;
|
|
}
|
|
|
|
void CPSong::set_instruments(bool p_instruments) {
|
|
|
|
variables.use_instruments=p_instruments;
|
|
|
|
|
|
}
|
|
bool CPSong::has_instruments() {
|
|
|
|
|
|
return variables.use_instruments;
|
|
|
|
}
|
|
|
|
void CPSong::set_linear_slides(bool p_linear_slides) {
|
|
|
|
variables.use_linear_slides=p_linear_slides;
|
|
|
|
|
|
}
|
|
bool CPSong::has_linear_slides() {
|
|
|
|
return variables.use_linear_slides;
|
|
|
|
|
|
}
|
|
|
|
void CPSong::set_old_effects(bool p_old_effects) {
|
|
|
|
variables.old_effects=p_old_effects;
|
|
|
|
|
|
}
|
|
bool CPSong::has_old_effects() {
|
|
|
|
return variables.old_effects;
|
|
}
|
|
|
|
void CPSong::set_compatible_gxx(bool p_compatible_gxx) {
|
|
|
|
|
|
variables.compatible_gxx=p_compatible_gxx;
|
|
}
|
|
bool CPSong::has_compatible_gxx() {
|
|
|
|
return variables.compatible_gxx;
|
|
|
|
}
|
|
|
|
void CPSong::set_speed(int p_speed) {
|
|
|
|
CP_ERR_COND(p_speed<MIN_SPEED);
|
|
CP_ERR_COND(p_speed>MAX_SPEED);
|
|
|
|
initial_variables.speed=p_speed;
|
|
|
|
} /* 1 .. 255 */
|
|
int CPSong::get_speed() {
|
|
|
|
return initial_variables.speed;
|
|
|
|
} /* 1 .. 255 */
|
|
|
|
void CPSong::set_tempo(int p_tempo) {
|
|
|
|
CP_ERR_COND( p_tempo<MIN_TEMPO );
|
|
CP_ERR_COND( p_tempo>MAX_TEMPO );
|
|
|
|
initial_variables.tempo=p_tempo;
|
|
|
|
} /* MIN_TEMPO .. MAX_TEMPO */
|
|
int CPSong::get_tempo() {
|
|
|
|
return initial_variables.tempo;
|
|
|
|
|
|
} /* MIN_TEMPO .. MAX_TEMPO */
|
|
|
|
void CPSong::set_channel_pan(int p_channel,int p_pan) {
|
|
|
|
CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
|
|
CP_FAIL_INDEX(p_pan,CHANNEL_MAX_PAN+1);
|
|
|
|
initial_variables.channel[p_channel].pan=p_pan;
|
|
|
|
} /* 0 .. CHANNEL_MAX_PAN */
|
|
int CPSong::get_channel_pan(int p_channel) {
|
|
|
|
CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
|
|
|
|
return initial_variables.channel[p_channel].pan;
|
|
}
|
|
|
|
void CPSong::set_channel_volume(int p_channel,int p_volume) {
|
|
|
|
CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
|
|
CP_FAIL_INDEX(p_volume,CHANNEL_MAX_VOLUME+1);
|
|
|
|
|
|
initial_variables.channel[p_channel].volume=p_volume;
|
|
|
|
|
|
} /* 0 .. CHANNEL_MAX_VOLUME */
|
|
|
|
|
|
int CPSong::get_channel_volume(int p_channel) {
|
|
|
|
CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
|
|
|
|
return initial_variables.channel[p_channel].volume;
|
|
|
|
}
|
|
|
|
void CPSong::set_channel_chorus(int p_channel,int p_chorus) {
|
|
|
|
CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
|
|
CP_FAIL_INDEX(p_chorus,CHANNEL_MAX_CHORUS+1);
|
|
|
|
|
|
initial_variables.channel[p_channel].chorus=p_chorus;
|
|
|
|
|
|
} /* 0 .. CHANNEL_MAX_CHORUS */
|
|
|
|
|
|
int CPSong::get_channel_chorus(int p_channel) {
|
|
|
|
CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
|
|
|
|
return initial_variables.channel[p_channel].chorus;
|
|
|
|
}
|
|
|
|
void CPSong::set_channel_reverb(int p_channel,int p_reverb) {
|
|
|
|
CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
|
|
CP_FAIL_INDEX(p_reverb,CHANNEL_MAX_REVERB+1);
|
|
|
|
|
|
initial_variables.channel[p_channel].reverb=p_reverb;
|
|
|
|
|
|
} /* 0 .. CHANNEL_MAX_CHORUS */
|
|
|
|
|
|
int CPSong::get_channel_reverb(int p_channel) {
|
|
|
|
CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
|
|
|
|
return initial_variables.channel[p_channel].reverb;
|
|
|
|
}
|
|
|
|
void CPSong::set_channel_surround(int p_channel,bool p_surround) {
|
|
|
|
CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
|
|
initial_variables.channel[p_channel].surround=p_surround;
|
|
|
|
}
|
|
bool CPSong::is_channel_surround(int p_channel) {
|
|
|
|
CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,false);
|
|
|
|
return initial_variables.channel[p_channel].surround;
|
|
|
|
|
|
}
|
|
|
|
void CPSong::set_channel_mute(int p_channel,bool p_mute) {
|
|
|
|
CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
|
|
|
|
initial_variables.channel[p_channel].mute=p_mute;
|
|
|
|
}
|
|
bool CPSong::is_channel_mute(int p_channel) {
|
|
|
|
CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,false);
|
|
|
|
return initial_variables.channel[p_channel].mute;
|
|
|
|
}
|
|
|
|
/* arrays of stuff */
|
|
|
|
CPPattern* CPSong::get_pattern(int p_pattern) {
|
|
|
|
CP_FAIL_INDEX_V(p_pattern,MAX_PATTERNS, NULL);
|
|
|
|
return &pattern[p_pattern];
|
|
|
|
}
|
|
CPSample* CPSong::get_sample(int p_sample) {
|
|
|
|
CP_FAIL_INDEX_V(p_sample,MAX_SAMPLES,NULL);
|
|
|
|
return &sample[p_sample];
|
|
|
|
|
|
}
|
|
CPInstrument* CPSong::get_instrument(int p_instrument) {
|
|
|
|
|
|
CP_FAIL_INDEX_V(p_instrument,MAX_INSTRUMENTS,NULL);
|
|
|
|
return &instrument[p_instrument];
|
|
|
|
}
|
|
|
|
int CPSong::get_order(int p_order) {
|
|
|
|
CP_FAIL_INDEX_V(p_order,MAX_ORDERS,CP_ORDER_NONE);
|
|
|
|
|
|
return order[p_order];
|
|
|
|
}
|
|
void CPSong::set_order(int p_order,int p_pattern) {
|
|
|
|
CP_FAIL_INDEX(p_order,MAX_ORDERS);
|
|
|
|
order[p_order]=p_pattern;
|
|
|
|
}
|
|
|
|
|
|
void CPSong::clear_instrument_with_samples(int p_instrument) {
|
|
|
|
CPInstrument *ins = get_instrument( p_instrument );
|
|
if (!ins)
|
|
return;
|
|
|
|
for (int i=0;i<CPNote::NOTES;i++) {
|
|
|
|
CPSample *s=get_sample( ins->get_sample_number( i ) );
|
|
|
|
if (!s)
|
|
continue;
|
|
|
|
if (s->get_sample_data().is_null())
|
|
continue;
|
|
|
|
s->reset();
|
|
}
|
|
ins->reset();
|
|
}
|
|
|
|
void CPSong::make_instrument_from_sample(int p_sample) {
|
|
|
|
if (!has_instruments())
|
|
return;
|
|
CP_ERR_COND(!get_sample( p_sample ));
|
|
|
|
for (int i=0;i<MAX_INSTRUMENTS;i++) {
|
|
|
|
|
|
CPInstrument *ins=get_instrument(i);
|
|
|
|
bool empty_slot=true;
|
|
for (int n=0;n<CPNote::NOTES;n++) {
|
|
|
|
if (ins->get_sample_number(n)<MAX_SAMPLES) {
|
|
|
|
empty_slot=false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!empty_slot)
|
|
continue;
|
|
|
|
for (int n=0;n<CPNote::NOTES;n++) {
|
|
|
|
ins->set_sample_number(n,p_sample);
|
|
ins->set_note_number(n,n);
|
|
}
|
|
|
|
ins->set_name( get_sample( p_sample )->get_name() );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void CPSong::make_instruments_from_samples() {
|
|
|
|
for (int i=0;i<MAX_SAMPLES;i++) {
|
|
|
|
CPInstrument *ins=get_instrument( i );
|
|
|
|
if (!ins)
|
|
continue;
|
|
|
|
ins->reset();
|
|
|
|
CPSample *s=get_sample( i );
|
|
|
|
if (!s)
|
|
continue;
|
|
|
|
ins->set_name( s->get_name() );
|
|
|
|
if (s->get_sample_data().is_null())
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for(int j=0;j<CPNote::NOTES;j++)
|
|
ins->set_sample_number( j, i );
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
void CPSong::reset(bool p_clear_patterns,bool p_clear_samples,bool p_clear_instruments,bool p_clear_variables) {
|
|
|
|
if (p_clear_variables) {
|
|
variables.name[0]=0;
|
|
variables.message[0]=0;
|
|
variables.row_highlight_major=16;
|
|
variables.row_highlight_minor=4;
|
|
variables.mixing_volume=48;
|
|
variables.old_effects=false;
|
|
if (p_clear_instruments) //should not be cleared, if not clearing instruments!!
|
|
variables.use_instruments=false;
|
|
variables.stereo_separation=128;
|
|
variables.use_linear_slides=true;
|
|
variables.use_stereo=true;
|
|
|
|
initial_variables.global_volume=128;
|
|
initial_variables.speed=6;
|
|
initial_variables.tempo=125;
|
|
|
|
for (int i=0;i<CPPattern::WIDTH;i++) {
|
|
|
|
initial_variables.channel[i].pan=32;
|
|
initial_variables.channel[i].volume=CHANNEL_MAX_VOLUME;
|
|
initial_variables.channel[i].mute=false;
|
|
initial_variables.channel[i].surround=false;
|
|
initial_variables.channel[i].chorus=0;
|
|
initial_variables.channel[i].reverb=0;
|
|
|
|
}
|
|
|
|
effects.chorus.delay_ms=6;
|
|
effects.chorus.separation_ms=3;
|
|
effects.chorus.depth_ms10=6,
|
|
effects.chorus.speed_hz10=5;
|
|
effects.reverb_mode=REVERB_MODE_ROOM;
|
|
}
|
|
|
|
if (p_clear_samples) {
|
|
for (int i=0;i<MAX_SAMPLES;i++)
|
|
get_sample(i)->reset();
|
|
}
|
|
|
|
if (p_clear_instruments) {
|
|
for (int i=0;i<MAX_INSTRUMENTS;i++)
|
|
get_instrument(i)->reset();
|
|
}
|
|
|
|
if (p_clear_patterns) {
|
|
for (int i=0;i<MAX_PATTERNS;i++)
|
|
get_pattern(i)->clear();
|
|
|
|
for (int i=0;i<MAX_ORDERS;i++)
|
|
set_order( i, CP_ORDER_NONE );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
CPSong::ReverbMode CPSong::get_reverb_mode() {
|
|
|
|
return effects.reverb_mode;
|
|
}
|
|
void CPSong::set_reverb_mode(ReverbMode p_mode) {
|
|
|
|
effects.reverb_mode=p_mode;
|
|
}
|
|
|
|
void CPSong::set_chorus_delay_ms(int p_amount) {
|
|
|
|
effects.chorus.delay_ms=p_amount;
|
|
}
|
|
void CPSong::set_chorus_separation_ms(int p_amount) {
|
|
|
|
effects.chorus.separation_ms=p_amount;
|
|
|
|
}
|
|
void CPSong::set_chorus_depth_ms10(int p_amount) {
|
|
|
|
effects.chorus.depth_ms10=p_amount;
|
|
|
|
}
|
|
void CPSong::set_chorus_speed_hz10(int p_amount) {
|
|
|
|
effects.chorus.speed_hz10=p_amount;
|
|
|
|
}
|
|
|
|
int CPSong::get_chorus_delay_ms() {
|
|
|
|
return effects.chorus.delay_ms;
|
|
|
|
}
|
|
int CPSong::get_chorus_separation_ms() {
|
|
|
|
return effects.chorus.separation_ms;
|
|
}
|
|
int CPSong::get_chorus_depth_ms10() {
|
|
|
|
return effects.chorus.depth_ms10;
|
|
|
|
}
|
|
int CPSong::get_chorus_speed_hz10() {
|
|
|
|
return effects.chorus.speed_hz10;
|
|
|
|
}
|
|
|
|
void CPSong::cleanup_unused_patterns() {
|
|
|
|
for (int i=0;i<MAX_PATTERNS;i++) {
|
|
|
|
bool used=false;
|
|
if (get_pattern(i)->is_empty())
|
|
continue;
|
|
|
|
for (int j=0;j<MAX_ORDERS;j++) {
|
|
|
|
if (get_order(j)==i) {
|
|
used=true;
|
|
|
|
}
|
|
}
|
|
|
|
if (!used)
|
|
get_pattern(i)->clear();
|
|
}
|
|
|
|
}
|
|
void CPSong::cleanup_unused_instruments(){
|
|
|
|
if (!has_instruments())
|
|
return;
|
|
|
|
bool instr_found[MAX_INSTRUMENTS];
|
|
for (int i=0;i<MAX_INSTRUMENTS;i++)
|
|
instr_found[i]=false;
|
|
|
|
for (int i=0;i<MAX_PATTERNS;i++) {
|
|
|
|
if (get_pattern(i)->is_empty())
|
|
continue;
|
|
|
|
for (int row=0;row<get_pattern(i)->get_length();row++) {
|
|
|
|
|
|
for (int col=0;col<CPPattern::WIDTH;col++) {
|
|
|
|
CPNote n;
|
|
n=get_pattern(i)->get_note( col,row );
|
|
|
|
if (n.instrument<MAX_INSTRUMENTS)
|
|
instr_found[n.instrument]=true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i=0;i<MAX_INSTRUMENTS;i++)
|
|
if (!instr_found[i])
|
|
get_instrument(i)->reset();
|
|
|
|
|
|
}
|
|
void CPSong::cleanup_unused_samples(){
|
|
|
|
if (!has_instruments())
|
|
return;
|
|
|
|
bool sample_found[MAX_SAMPLES];
|
|
for (int i=0;i<MAX_INSTRUMENTS;i++)
|
|
sample_found[i]=false;
|
|
|
|
for (int i=0;i<MAX_PATTERNS;i++) {
|
|
|
|
if (get_pattern(i)->is_empty())
|
|
continue;
|
|
|
|
|
|
for (int row=0;row<get_pattern(i)->get_length();row++) {
|
|
|
|
|
|
for (int col=0;col<CPPattern::WIDTH;col++) {
|
|
|
|
CPNote n;
|
|
n=get_pattern(i)->get_note( col,row );
|
|
|
|
if (n.instrument>=MAX_SAMPLES)
|
|
continue;
|
|
|
|
if (has_instruments()) {
|
|
|
|
for (int nt=0;nt<CPNote::NOTES;nt++) {
|
|
|
|
int smp=get_instrument(n.instrument)->get_sample_number(nt);
|
|
if (smp<MAX_SAMPLES)
|
|
sample_found[smp]=true;
|
|
}
|
|
|
|
} else {
|
|
if (n.instrument<MAX_SAMPLES)
|
|
sample_found[n.instrument]=true;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i=0;i<MAX_SAMPLES;i++)
|
|
if (!sample_found[i])
|
|
get_sample(i)->reset();
|
|
|
|
}
|
|
void CPSong::cleanup_unused_orders(){
|
|
|
|
bool finito=false;
|
|
for (int j=0;j<MAX_ORDERS;j++) {
|
|
|
|
|
|
if (get_order(j)==CP_ORDER_NONE)
|
|
finito=true;
|
|
if (finito)
|
|
set_order(j,CP_ORDER_NONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void CPSong::clear_all_default_pan() {
|
|
|
|
for (int i=0;i<MAX_INSTRUMENTS;i++)
|
|
get_instrument(i)->set_pan_default_enabled( false ); //die!
|
|
|
|
for (int i=0;i<MAX_SAMPLES;i++)
|
|
get_sample(i)->set_pan_enabled( false ); //die!
|
|
|
|
}
|
|
|
|
|
|
void CPSong::clear_all_default_vol(){
|
|
|
|
for (int i=0;i<MAX_SAMPLES;i++)
|
|
get_sample(i)->set_default_volume( 64 ); //die!
|
|
for (int i=0;i<MAX_INSTRUMENTS;i++)
|
|
get_instrument(i)->set_volume_global_amount( CPInstrument::MAX_VOLUME );
|
|
|
|
}
|
|
|
|
|
|
int CPSong::get_order_in_use_count() {
|
|
|
|
|
|
int order_count = 0;
|
|
|
|
for (int i=(MAX_ORDERS-1);i>=0;i--) {
|
|
|
|
|
|
if (get_order(i)!=CP_ORDER_NONE) {
|
|
order_count=i+1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return order_count;
|
|
}
|
|
int CPSong::get_pattern_in_use_count() {
|
|
|
|
|
|
int pattern_count=0;
|
|
|
|
for (int i=(CPSong::MAX_PATTERNS-1);i>=0;i--) {
|
|
|
|
|
|
if (!get_pattern(i)->is_empty()) {
|
|
pattern_count=i+1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pattern_count;
|
|
}
|
|
|
|
int CPSong::get_instrument_in_use_count() {
|
|
|
|
int instrument_count=0;
|
|
|
|
for (int i=(CPSong::MAX_INSTRUMENTS-1);i>=0;i--) {
|
|
|
|
CPInstrument *ins = get_instrument(i);
|
|
bool in_use=false;
|
|
|
|
for (int s = 0 ; s < CPNote::NOTES ; s++ ) {
|
|
|
|
int smp_idx = ins->get_sample_number(s);
|
|
if (smp_idx<0 || smp_idx>=CPSong::MAX_SAMPLES)
|
|
continue;
|
|
|
|
if (!get_sample(smp_idx)->get_sample_data().is_null()) {
|
|
in_use=true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (in_use) {
|
|
instrument_count=i+1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return instrument_count;
|
|
}
|
|
#include <stdio.h>
|
|
int CPSong::get_channels_in_use() {
|
|
|
|
int max=0;
|
|
|
|
for (int p=0;p<CPSong::MAX_PATTERNS;p++) {
|
|
|
|
CPPattern *pat = get_pattern(p);
|
|
if (pat->is_empty())
|
|
continue;
|
|
|
|
|
|
for (int c=(CPPattern::WIDTH-1);c>=0;c--) {
|
|
|
|
if (c<max)
|
|
break;
|
|
|
|
bool has_note=false;
|
|
for (int r=0;r<pat->get_length();r++) {
|
|
|
|
CPNote n = pat->get_note( c, r );
|
|
if (!n.is_empty()) {
|
|
has_note=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (has_note) {
|
|
|
|
max=c+1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
|
|
void CPSong::separate_in_one_sample_instruments(int p_instrument) {
|
|
|
|
CP_ERR_COND( !variables.use_instruments );
|
|
CP_FAIL_INDEX( p_instrument, MAX_INSTRUMENTS );
|
|
|
|
int remapped_count=0;
|
|
|
|
signed char remap[MAX_SAMPLES];
|
|
|
|
for (int i=0;i<MAX_SAMPLES;i++) {
|
|
|
|
remap[i]=-1;
|
|
}
|
|
|
|
/* Find remaps */
|
|
CPInstrument *ins=get_instrument(p_instrument);
|
|
for (int i=0;i<CPNote::NOTES;i++) {
|
|
|
|
int sn = ins->get_sample_number(i);
|
|
|
|
// check for unusable sample
|
|
if (sn<0 || sn>=MAX_SAMPLES || get_sample(sn)->get_sample_data().is_null())
|
|
continue;
|
|
printf("sample %i\n",sn);
|
|
if ( remap[sn] !=-1 ) {
|
|
printf("already mapped to %i\n",remap[sn]);
|
|
continue;
|
|
}
|
|
|
|
printf("isn't remapped\n");
|
|
|
|
// find remap
|
|
|
|
for (int j=0;j<MAX_INSTRUMENTS;j++) {
|
|
|
|
if (!get_instrument(j)->is_empty())
|
|
continue;
|
|
|
|
printf("map to %i\n",j);
|
|
|
|
//copy
|
|
*get_instrument(j)=*ins;
|
|
|
|
// assign samples
|
|
for (int k=0;k<CPNote::NOTES;k++) {
|
|
|
|
get_instrument(j)->set_note_number(k,k);
|
|
get_instrument(j)->set_sample_number(k,sn);
|
|
}
|
|
remap[sn]=j;
|
|
remapped_count++;
|
|
break;
|
|
}
|
|
|
|
CP_ERR_COND(remap[sn]==-1); // no more free instruments
|
|
}
|
|
|
|
printf("remapped %i\n",remapped_count);
|
|
|
|
if (remapped_count<2) {
|
|
//undo if only one is remapped
|
|
for (int i=0;i<MAX_SAMPLES;i++) {
|
|
|
|
if (remap[i]!=-1) {
|
|
|
|
get_instrument(remap[i])->reset();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* remap all song */
|
|
|
|
for (int p=0;p<CPSong::MAX_PATTERNS;p++) {
|
|
|
|
CPPattern *pat = get_pattern(p);
|
|
if (pat->is_empty())
|
|
continue;
|
|
|
|
|
|
for (int c=0;c<CPPattern::WIDTH;c++) {
|
|
|
|
for (int r=0;r<pat->get_length();r++) {
|
|
|
|
CPNote n = pat->get_note(c,r);
|
|
if (n.note<CPNote::NOTES && n.instrument==p_instrument) {
|
|
|
|
int sn = ins->get_sample_number(n.note);
|
|
if (remap[sn]==-1)
|
|
pat->set_note(c,r,CPNote());
|
|
else {
|
|
|
|
n.instrument=remap[sn];
|
|
pat->set_note(c,r,n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ins->reset();
|
|
|
|
}
|
|
|
|
|
|
CPSong::CPSong() {
|
|
|
|
reset();
|
|
}
|
|
CPSong::~CPSong() {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_song_next_order_idx(CPSong *p_song, int p_order_idx) {
|
|
|
|
int baseorder,order_counter;
|
|
|
|
order_counter=-1;
|
|
|
|
baseorder=p_order_idx;
|
|
|
|
do {
|
|
|
|
baseorder++;
|
|
if ( baseorder>(CPSong::MAX_ORDERS-1) ) baseorder=0;
|
|
order_counter++;
|
|
|
|
} while ( (p_song->get_order(baseorder)>=(CPSong::MAX_PATTERNS) ) && (order_counter<CPSong::MAX_ORDERS) );
|
|
|
|
|
|
if (order_counter==CPSong::MAX_ORDERS) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return baseorder;
|
|
}
|
|
|
|
}
|