d8223ffa75
That year should bring the long-awaited OpenGL ES 3.0 compatible renderer
with state-of-the-art rendering techniques tuned to work as low as middle
end handheld devices - without compromising with the possibilities given
for higher end desktop games of course. Great times ahead for the Godot
community and the gamers that will play our games!
(cherry picked from commit c7bc44d5ad
)
1233 lines
37 KiB
C++
1233 lines
37 KiB
C++
/*************************************************************************/
|
|
/* cp_player_data_effects.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_player_data.h"
|
|
|
|
|
|
|
|
/**********************
|
|
complex effects
|
|
***********************/
|
|
#define RANDOM_MAX 2147483647
|
|
|
|
static inline int32_t cp_random_generate(int32_t *seed) {
|
|
int32_t k;
|
|
int32_t s = (int32_t)(*seed);
|
|
if (s == 0)
|
|
s = 0x12345987;
|
|
k = s / 127773;
|
|
s = 16807 * (s - k * 127773) - 2836 * k;
|
|
if (s < 0)
|
|
s += 2147483647;
|
|
(*seed) = (int32_t)s;
|
|
return (int32_t)(s & RANDOM_MAX);
|
|
}
|
|
|
|
|
|
|
|
void CPPlayer::do_vibrato(int p_track,bool fine) {
|
|
|
|
uint8_t q;
|
|
uint16_t temp=0;
|
|
|
|
if ((control.ticks_counter==0) && control.channel[p_track].row_has_note) control.channel[p_track].vibrato_position=0;
|
|
|
|
q=(control.channel[p_track].vibrato_position>>2)&0x1f;
|
|
|
|
switch (control.channel[p_track].vibrato_type) {
|
|
case 0: /* sine */
|
|
temp=vibrato_table[q];
|
|
break;
|
|
case 1: /* square wave */
|
|
temp=255;
|
|
break;
|
|
case 2: /* ramp down */
|
|
q<<=3;
|
|
if (control.channel[p_track].vibrato_position<0) q=255-q;
|
|
temp=q;
|
|
break;
|
|
case 3: /* random */
|
|
temp=cp_random_generate(&control.random_seed) %256;//getrandom(256);
|
|
break;
|
|
}
|
|
|
|
temp*=control.channel[p_track].vibrato_depth;
|
|
|
|
if (song->has_old_effects()) {
|
|
|
|
temp>>=7;
|
|
} else {
|
|
|
|
temp>>=8;
|
|
}
|
|
|
|
if (!fine) temp<<=2;
|
|
|
|
if (control.channel[p_track].vibrato_position>=0) {
|
|
|
|
control.channel[p_track].period=control.channel[p_track].aux_period+temp;
|
|
} else {
|
|
|
|
control.channel[p_track].period=control.channel[p_track].aux_period-temp;
|
|
}
|
|
|
|
if (!song->has_old_effects() || control.ticks_counter) control.channel[p_track].vibrato_position+=control.channel[p_track].vibrato_speed;
|
|
}
|
|
|
|
|
|
void CPPlayer::do_pitch_slide_down(int p_track,uint8_t inf) {
|
|
|
|
uint8_t hi,lo;
|
|
|
|
if (inf) control.channel[p_track].pitch_slide_info=inf;
|
|
else inf=control.channel[p_track].pitch_slide_info;
|
|
|
|
hi=inf>>4;
|
|
lo=inf&0xf;
|
|
|
|
if (hi==0xf) {
|
|
|
|
if (!control.ticks_counter) control.channel[p_track].aux_period+=(uint16_t)lo<<2;
|
|
} else if (hi==0xe) {
|
|
|
|
if (!control.ticks_counter) control.channel[p_track].aux_period+=lo;
|
|
} else {
|
|
|
|
if (control.ticks_counter) control.channel[p_track].aux_period+=(uint16_t)inf<<2;
|
|
}
|
|
}
|
|
|
|
void CPPlayer::do_pitch_slide_up(int p_track,uint8_t inf) {
|
|
|
|
uint8_t hi,lo;
|
|
|
|
if (inf) control.channel[p_track].pitch_slide_info=inf;
|
|
else inf=control.channel[p_track].pitch_slide_info;
|
|
|
|
hi=inf>>4;
|
|
lo=inf&0xf;
|
|
|
|
if (hi==0xf) {
|
|
|
|
if (!control.ticks_counter) control.channel[p_track].aux_period-=(uint16_t)lo<<2;
|
|
} else if (hi==0xe) {
|
|
|
|
if (!control.ticks_counter) control.channel[p_track].aux_period-=lo;
|
|
} else {
|
|
|
|
if (control.ticks_counter) control.channel[p_track].aux_period-=(uint16_t)inf<<2;
|
|
}
|
|
}
|
|
|
|
void CPPlayer::do_pitch_slide_to_note(int p_track) {
|
|
|
|
if (control.ticks_counter) {
|
|
int dist;
|
|
|
|
/* We have to slide a->period towards a->wantedperiod, compute the
|
|
difference between those two values */
|
|
dist=control.channel[p_track].period-control.channel[p_track].slide_to_period;
|
|
|
|
/* if they are equal or if portamentospeed is too big... */
|
|
if ((!dist)||((control.channel[p_track].portamento_speed<<2)>cp_intabs(dist))) {
|
|
/* ... make tmpperiod equal tperiod */
|
|
control.channel[p_track].aux_period=control.channel[p_track].period=control.channel[p_track].slide_to_period;
|
|
} else {
|
|
|
|
if (dist>0) {
|
|
|
|
control.channel[p_track].aux_period-=control.channel[p_track].portamento_speed<<2;
|
|
control.channel[p_track].period-=control.channel[p_track].portamento_speed<<2; /* dist>0 slide up */
|
|
} else {
|
|
control.channel[p_track].aux_period+=control.channel[p_track].portamento_speed<<2;
|
|
control.channel[p_track].period+=control.channel[p_track].portamento_speed<<2; /* dist<0 slide down */
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
control.channel[p_track].aux_period=control.channel[p_track].period;
|
|
}
|
|
}
|
|
|
|
void CPPlayer::do_tremor(int p_track) {
|
|
|
|
uint8_t on,off,inf;
|
|
|
|
inf=control.channel[p_track].current_parameter;
|
|
|
|
if (inf) {
|
|
control.channel[p_track].tremor_info=inf;
|
|
} else {
|
|
inf= control.channel[p_track].tremor_info;
|
|
if (!inf) return;
|
|
}
|
|
|
|
//if (!control.ticks_counter) return;
|
|
|
|
on=(inf>>4);
|
|
off=(inf&0xf);
|
|
|
|
control.channel[p_track].tremor_position%=(on+off);
|
|
control.channel[p_track].volume=(control.channel[p_track].tremor_position<on)?control.channel[p_track].aux_volume:0;
|
|
control.channel[p_track].tremor_position++;
|
|
}
|
|
|
|
void CPPlayer::do_pan_slide(int p_track) {
|
|
|
|
uint8_t lo,hi,inf;
|
|
int16_t pan;
|
|
|
|
inf=control.channel[p_track].current_parameter;
|
|
|
|
if (inf) control.channel[p_track].channel_pan_slide_info=inf;
|
|
else inf=control.channel[p_track].channel_pan_slide_info;
|
|
|
|
lo=inf&0xf;
|
|
hi=inf>>4;
|
|
|
|
pan=(control.channel[p_track].panning==PAN_SURROUND)?PAN_CENTER:control.channel[p_track].panning;
|
|
|
|
if (!hi)
|
|
pan+=lo<<2;
|
|
else
|
|
if (!lo) {
|
|
pan-=hi<<2;
|
|
} else
|
|
if (hi==0xf) {
|
|
if (!control.ticks_counter) pan+=lo<<2;
|
|
} else
|
|
if (lo==0xf) {
|
|
if (!control.ticks_counter) pan-=hi<<2;
|
|
}
|
|
//this sets both chan & voice paning
|
|
control.channel[p_track].panning=(pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);
|
|
control.channel[p_track].channel_panning=control.channel[p_track].panning;
|
|
}
|
|
|
|
void CPPlayer::do_volume_slide(int p_track,int inf) {
|
|
|
|
uint8_t hi,lo;
|
|
|
|
lo=inf&0xf;
|
|
hi=inf>>4;
|
|
|
|
if (!lo) {
|
|
|
|
if ((control.ticks_counter)) control.channel[p_track].aux_volume+=hi;
|
|
|
|
} else if (!hi) {
|
|
|
|
if ((control.ticks_counter)) control.channel[p_track].aux_volume-=lo;
|
|
|
|
} else if (lo==0xf) {
|
|
|
|
if (!control.ticks_counter) control.channel[p_track].aux_volume+=(hi?hi:0xf);
|
|
} else if (hi==0xf) {
|
|
|
|
if (!control.ticks_counter) control.channel[p_track].aux_volume-=(lo?lo:0xf);
|
|
} else return;
|
|
|
|
if (control.channel[p_track].aux_volume<0) {
|
|
|
|
control.channel[p_track].aux_volume=0;
|
|
} else if (control.channel[p_track].aux_volume>64) {
|
|
|
|
control.channel[p_track].aux_volume=64;
|
|
}
|
|
}
|
|
|
|
void CPPlayer::do_channel_volume_slide(int p_track) {
|
|
|
|
uint8_t lo, hi,inf;
|
|
|
|
inf=control.channel[p_track].current_parameter;
|
|
|
|
if (inf) control.channel[p_track].channel_volume_slide_info=inf;
|
|
inf=control.channel[p_track].channel_volume_slide_info;
|
|
|
|
lo=inf&0xf;
|
|
hi=inf>>4;
|
|
|
|
if (!hi)
|
|
control.channel[p_track].channel_volume-=lo;
|
|
else
|
|
if (!lo) {
|
|
control.channel[p_track].channel_volume+=hi;
|
|
} else
|
|
if (hi==0xf) {
|
|
if (!control.ticks_counter) control.channel[p_track].channel_volume-=lo;
|
|
} else
|
|
if (lo==0xf) {
|
|
if (!control.ticks_counter) control.channel[p_track].channel_volume+=hi;
|
|
}
|
|
|
|
if (control.channel[p_track].channel_volume<0) control.channel[p_track].channel_volume=0;
|
|
if (control.channel[p_track].channel_volume>64) control.channel[p_track].channel_volume=64;
|
|
}
|
|
|
|
void CPPlayer::do_tremolo(int p_track) {
|
|
|
|
uint8_t q;
|
|
int16_t temp=0;
|
|
|
|
if ((control.ticks_counter==0) && control.channel[p_track].row_has_note) control.channel[p_track].tremolo_position=0;
|
|
|
|
q=(control.channel[p_track].tremolo_position>>2)&0x1f;
|
|
|
|
switch (control.channel[p_track].tremolo_type) {
|
|
case 0: /* sine */
|
|
temp=vibrato_table[q];
|
|
break;
|
|
case 1: /* ramp down */
|
|
q<<=3;
|
|
if (control.channel[p_track].tremolo_position<0) q=255-q;
|
|
temp=q;
|
|
break;
|
|
case 2: /* square wave */
|
|
temp=255;
|
|
break;
|
|
case 3: /* random */
|
|
temp=cp_random_generate(&control.random_seed) % 256;//getrandom(256);
|
|
break;
|
|
}
|
|
|
|
temp*=control.channel[p_track].tremolo_depth;
|
|
temp>>=7;
|
|
|
|
|
|
|
|
if (control.channel[p_track].tremolo_position>=0) {
|
|
|
|
|
|
control.channel[p_track].volume=control.channel[p_track].aux_volume+temp;
|
|
if (control.channel[p_track].volume>64) control.channel[p_track].volume=64;
|
|
} else {
|
|
|
|
control.channel[p_track].volume=control.channel[p_track].aux_volume-temp;
|
|
if (control.channel[p_track].volume<0) control.channel[p_track].volume=0;
|
|
}
|
|
|
|
/*if (control.ticks_counter)*/ control.channel[p_track].tremolo_position+=control.channel[p_track].tremolo_speed;
|
|
|
|
}
|
|
|
|
void CPPlayer::do_arpegio(int p_track) {
|
|
|
|
uint8_t note,dat;
|
|
//note=control.channel[p_track].note;
|
|
note=0;
|
|
|
|
if (control.channel[p_track].current_parameter) {
|
|
|
|
control.channel[p_track].arpegio_info=control.channel[p_track].current_parameter;
|
|
}
|
|
|
|
dat=control.channel[p_track].arpegio_info;
|
|
|
|
if (dat) {
|
|
|
|
switch (control.ticks_counter%3) {
|
|
|
|
case 1: {
|
|
|
|
note+=(dat>>4);
|
|
|
|
} break;
|
|
case 2: {
|
|
|
|
note+=(dat&0xf);
|
|
} break;
|
|
}
|
|
|
|
if (song->has_linear_slides()) {
|
|
|
|
control.channel[p_track].period=control.channel[p_track].aux_period-cp_intabs(get_period((uint16_t)46,0)-get_period((uint16_t)44,0))*note;
|
|
} else if (control.channel[p_track].sample_ptr) {
|
|
|
|
control.channel[p_track].period=get_period( (((uint16_t)control.channel[p_track].note)+note)<<1,CPSampleManager::get_singleton()->get_c5_freq( (control.channel[p_track].sample_ptr->get_sample_data())));
|
|
}
|
|
|
|
control.channel[p_track].has_own_period=true;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void CPPlayer::do_retrig(int p_track) {
|
|
|
|
uint8_t inf;
|
|
|
|
inf=control.channel[p_track].current_parameter;
|
|
|
|
if (inf) {
|
|
|
|
control.channel[p_track].retrig_volslide=inf>>4;
|
|
control.channel[p_track].retrig_speed=inf&0xf;
|
|
}
|
|
|
|
/* only retrigger if low nibble > 0 */
|
|
if ( control.channel[p_track].retrig_speed>0) {
|
|
|
|
if ( !control.channel[p_track].retrig_counter ) {
|
|
/* when retrig counter reaches 0, reset counter and restart the
|
|
sample */
|
|
if (control.channel[p_track].kick!=KICK_NOTE) control.channel[p_track].kick=KICK_NOTEOFF;
|
|
control.channel[p_track].retrig_counter=control.channel[p_track].retrig_speed;
|
|
|
|
|
|
if ((control.ticks_counter)/*||(pf->flags&UF_S3MSLIDES)*/) {
|
|
switch (control.channel[p_track].retrig_volslide) {
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
control.channel[p_track].aux_volume-=(1<<(control.channel[p_track].retrig_volslide-1));
|
|
break;
|
|
case 6:
|
|
control.channel[p_track].aux_volume=(2*control.channel[p_track].aux_volume)/3;
|
|
break;
|
|
case 7:
|
|
control.channel[p_track].aux_volume>>=1;
|
|
break;
|
|
case 9:
|
|
case 0xa:
|
|
case 0xb:
|
|
case 0xc:
|
|
case 0xd:
|
|
control.channel[p_track].aux_volume+=(1<<(control.channel[p_track].retrig_volslide-9));
|
|
break;
|
|
case 0xe:
|
|
control.channel[p_track].aux_volume=(3*control.channel[p_track].aux_volume)>>1;
|
|
break;
|
|
case 0xf:
|
|
control.channel[p_track].aux_volume=control.channel[p_track].aux_volume<<1;
|
|
break;
|
|
}
|
|
if (control.channel[p_track].aux_volume<0) control.channel[p_track].aux_volume=0;
|
|
else if (control.channel[p_track].aux_volume>64) control.channel[p_track].aux_volume=64;
|
|
}
|
|
}
|
|
control.channel[p_track].retrig_counter--; /* countdown */
|
|
}
|
|
}
|
|
|
|
void CPPlayer::do_global_volume_slide(int p_track) {
|
|
|
|
uint8_t lo,hi,inf;
|
|
|
|
inf=control.channel[p_track].current_parameter;
|
|
|
|
if (inf) control.channel[p_track].global_volume_slide_info=inf;
|
|
inf=control.channel[p_track].global_volume_slide_info;
|
|
|
|
lo=inf&0xf;
|
|
hi=inf>>4;
|
|
|
|
if (!lo) {
|
|
if (control.ticks_counter) control.global_volume+=hi;
|
|
} else
|
|
if (!hi) {
|
|
if (control.ticks_counter) control.global_volume-=lo;
|
|
} else
|
|
if (lo==0xf) {
|
|
if (!control.ticks_counter) control.global_volume+=hi;
|
|
} else
|
|
if (hi==0xf) {
|
|
if (!control.ticks_counter) control.global_volume-=lo;
|
|
}
|
|
|
|
if (control.global_volume<0) control.global_volume=0;
|
|
if (control.global_volume>128) control.global_volume=128;
|
|
}
|
|
|
|
void CPPlayer::do_panbrello(int p_track) {
|
|
|
|
uint8_t q;
|
|
int32_t temp=0;
|
|
|
|
q=control.channel[p_track].panbrello_position;
|
|
|
|
switch (control.channel[p_track].panbrello_type) {
|
|
case 0: {/* sine */
|
|
temp=panbrello_table[q];
|
|
} break;
|
|
case 1: {/* square wave */
|
|
temp=(q<0x80)?64:0;
|
|
} break;
|
|
case 2: {/* ramp down */
|
|
q<<=3;
|
|
temp=q;
|
|
} break;
|
|
case 3: {/* random */
|
|
if (control.channel[p_track].panbrello_position>=control.channel[p_track].panbrello_speed) {
|
|
control.channel[p_track].panbrello_position=0;
|
|
temp=cp_random_generate(&control.random_seed)%256;//getrandom(256);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
|
|
|
|
temp=temp*(int)control.channel[p_track].panbrello_depth/0xF;
|
|
temp<<=1;
|
|
if (control.channel[p_track].channel_panning!=PAN_SURROUND)
|
|
temp+=control.channel[p_track].channel_panning;
|
|
|
|
control.channel[p_track].panning=(temp<PAN_LEFT)?PAN_LEFT:(temp>PAN_RIGHT?PAN_RIGHT:temp);
|
|
control.channel[p_track].panbrello_position+=control.channel[p_track].panbrello_speed;
|
|
}
|
|
|
|
/******************
|
|
S effect
|
|
*******************/
|
|
|
|
|
|
void CPPlayer::do_effect_S(int p_track) {
|
|
|
|
uint8_t inf,c,dat;
|
|
|
|
dat=control.channel[p_track].current_parameter;
|
|
|
|
inf=dat&0xf;
|
|
c=dat>>4;
|
|
|
|
if (!dat) {
|
|
c=control.channel[p_track].current_S_effect;
|
|
inf=control.channel[p_track].current_S_data;
|
|
} else {
|
|
control.channel[p_track].current_S_effect=c;
|
|
control.channel[p_track].current_S_data=inf;
|
|
}
|
|
|
|
switch (c) {
|
|
case 1: {/* S1x set glissando voice */
|
|
// this is unsupported in IT!
|
|
|
|
control.channel[p_track].chorus_send=inf*0xFF/0xF;
|
|
|
|
}break;
|
|
case 2: /* S2x set finetune */
|
|
//Also not supported!
|
|
break;
|
|
case 3: /* S3x set vibrato waveform */
|
|
if (inf<4) control.channel[p_track].vibrato_type=inf;
|
|
break;
|
|
case 4: /* S4x set tremolo waveform */
|
|
if (inf<4) control.channel[p_track].tremolo_type=inf;
|
|
break;
|
|
case 5: /* S5x panbrello */
|
|
if (inf<4) control.channel[p_track].panbrello_type=inf;
|
|
break;
|
|
case 6: {/* S6x delay x number of frames (patdly) */
|
|
|
|
if (control.ticks_counter) break;
|
|
if (!control.pattern_delay_2) control.pattern_delay_1=inf+1; /* only once, when vbtick=0 */
|
|
|
|
} break;
|
|
case 7: /* S7x instrument / NNA commands */
|
|
|
|
if (!song->has_instruments())
|
|
break;
|
|
switch(inf) {
|
|
|
|
case 0x3: {
|
|
|
|
control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_CUT;
|
|
} break;
|
|
case 0x4: {
|
|
|
|
control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_CONTINUE;
|
|
} break;
|
|
case 0x5: {
|
|
|
|
control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_OFF;
|
|
} break;
|
|
case 0x6: {
|
|
|
|
control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_FADE;
|
|
} break;
|
|
case 0x7: {
|
|
|
|
if (control.channel[p_track].slave_voice)
|
|
control.channel[p_track].slave_voice->volume_envelope_ctrl.active=false;
|
|
} break;
|
|
case 0x8: {
|
|
|
|
if (control.channel[p_track].slave_voice)
|
|
control.channel[p_track].slave_voice->volume_envelope_ctrl.active=true;
|
|
|
|
} break;
|
|
case 0x9: {
|
|
|
|
if (control.channel[p_track].slave_voice)
|
|
control.channel[p_track].slave_voice->panning_envelope_ctrl.active=false;
|
|
|
|
} break;
|
|
case 0xA: {
|
|
|
|
if (control.channel[p_track].slave_voice)
|
|
control.channel[p_track].slave_voice->panning_envelope_ctrl.active=true;
|
|
|
|
} break;
|
|
case 0xB: {
|
|
if (control.channel[p_track].slave_voice)
|
|
control.channel[p_track].slave_voice->pitch_envelope_ctrl.active=false;
|
|
|
|
} break;
|
|
case 0xC: {
|
|
|
|
if (control.channel[p_track].slave_voice)
|
|
control.channel[p_track].slave_voice->pitch_envelope_ctrl.active=true;
|
|
|
|
} break;
|
|
|
|
} break;
|
|
|
|
break;
|
|
case 8: {/* S8x set panning position */
|
|
|
|
// if (pf->panflag) {
|
|
if (inf<=8) inf<<=4;
|
|
else inf*=17;
|
|
control.channel[p_track].panning=control.channel[p_track].channel_panning=inf;
|
|
// }
|
|
} break;
|
|
|
|
case 9: { /* S9x set surround sound */
|
|
//if (pf->panflag)
|
|
control.channel[p_track].panning=control.channel[p_track].channel_panning=PAN_SURROUND;
|
|
} break;
|
|
case 0xA:{ /* SAy set high order sample offset yxx00h */
|
|
|
|
if (control.channel[p_track].current_parameter) control.channel[p_track].hi_offset=(int32_t)inf<<16;
|
|
control.channel[p_track].sample_start_index=control.channel[p_track].hi_offset|control.channel[p_track].lo_offset;
|
|
} break;
|
|
case 0xB: { /* SBx pattern loop */
|
|
if (control.ticks_counter) break;
|
|
|
|
if (inf) { /* set reppos or repcnt ? */
|
|
/* set repcnt, so check if repcnt already is set, which means we
|
|
are already looping */
|
|
if (control.channel[p_track].pattern_loop_count>0)
|
|
control.channel[p_track].pattern_loop_count--; /* already looping, decrease counter */
|
|
else {
|
|
control.channel[p_track].pattern_loop_count=inf; /* not yet looping, so set repcnt */
|
|
}
|
|
|
|
if (control.channel[p_track].pattern_loop_count>0) { /* jump to reppos if repcnt>0 */
|
|
|
|
control.position=control.previous_position; // This will also anulate any Cxx or break..
|
|
|
|
control.position.current_row=control.channel[p_track].pattern_loop_position;
|
|
control.position.forbid_jump=true;
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
control.channel[p_track].pattern_loop_position=control.position.current_row-1;
|
|
}
|
|
|
|
} break;
|
|
case 0xC: { /* SCx notecut */
|
|
|
|
if (control.ticks_counter>=inf) {
|
|
|
|
control.channel[p_track].aux_volume=0;
|
|
control.channel[p_track].note_end_flags|=END_NOTE_OFF;
|
|
control.channel[p_track].note_end_flags|=END_NOTE_KILL;
|
|
}
|
|
} break;
|
|
case 0xD: {/* SDx notedelay */
|
|
|
|
if (!control.ticks_counter) {
|
|
|
|
control.channel[p_track].note_delay=inf;
|
|
|
|
} else if (control.channel[p_track].note_delay) {
|
|
|
|
control.channel[p_track].note_delay--;
|
|
}
|
|
|
|
} break;
|
|
case 0xF: {/* SEx patterndelay */
|
|
|
|
if (control.ticks_counter) break;
|
|
if (!control.pattern_delay_2) control.pattern_delay_1=inf+1; /* only once, when vbtick=0 */
|
|
|
|
} break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********************
|
|
volume effects
|
|
**********************/
|
|
|
|
void CPPlayer::run_volume_column_effects(int p_track) {
|
|
|
|
uint8_t param=control.channel[p_track].current_volume_parameter;
|
|
|
|
|
|
switch ('A'+control.channel[p_track].current_volume_command) {
|
|
|
|
case 'A': {
|
|
|
|
if (param>0) control.channel[p_track].volcol_volume_slide=param;
|
|
else param=control.channel[p_track].volcol_volume_slide;
|
|
|
|
do_volume_slide(p_track,param*0x10+0xF);
|
|
|
|
} break;
|
|
case 'B': {
|
|
|
|
if (param>0) control.channel[p_track].volcol_volume_slide=param;
|
|
else param=control.channel[p_track].volcol_volume_slide;
|
|
|
|
do_volume_slide(p_track,0xF0+param);
|
|
|
|
} break;
|
|
case 'C': {
|
|
|
|
if (param>0) control.channel[p_track].volcol_volume_slide=param;
|
|
else param=control.channel[p_track].volcol_volume_slide;
|
|
|
|
do_volume_slide(p_track,param*0x10);
|
|
} break;
|
|
case 'D': {
|
|
|
|
if (param>0) control.channel[p_track].volcol_volume_slide=param;
|
|
else param=control.channel[p_track].volcol_volume_slide;
|
|
do_volume_slide(p_track,param);
|
|
|
|
} break;
|
|
case 'E': {
|
|
|
|
do_pitch_slide_down(p_track,param<<2);
|
|
} break;
|
|
case 'F': {
|
|
|
|
do_pitch_slide_up(p_track,param<<2);
|
|
} break;
|
|
case 'G': {
|
|
|
|
const uint8_t slide_table[]={0,1,4,8,16,32,64,96,128,255};
|
|
if (param) {
|
|
|
|
control.channel[p_track].portamento_speed=slide_table[param];
|
|
}
|
|
|
|
if (control.channel[p_track].period && (control.channel[p_track].old_note<=120)) {
|
|
|
|
if ( (!control.ticks_counter) && (control.channel[p_track].new_instrument) ){
|
|
|
|
//control.channel[p_track].kick=KICK_NOTE;
|
|
//control.channel[p_track].sample_start_index=0; // < am i stupid?
|
|
} else {
|
|
|
|
control.channel[p_track].kick=(control.channel[p_track].kick==KICK_NOTE)?KICK_ENVELOPE:KICK_NOTHING;
|
|
do_pitch_slide_to_note(p_track);
|
|
control.channel[p_track].has_own_period=true;
|
|
}
|
|
|
|
}
|
|
} break;
|
|
case 'H': {
|
|
|
|
|
|
if (!control.ticks_counter) {
|
|
if (param&0x0f) control.channel[p_track].vibrato_depth=param;
|
|
}
|
|
control.channel[p_track].doing_vibrato=true;
|
|
if (control.external_vibrato) break;
|
|
if (control.channel[p_track].period) {
|
|
|
|
do_vibrato(p_track,false);
|
|
control.channel[p_track].has_own_period=true;
|
|
}
|
|
|
|
} break;
|
|
}
|
|
}
|
|
/*********************
|
|
table
|
|
**********************/
|
|
|
|
|
|
void CPPlayer::run_effects(int p_track) {
|
|
|
|
switch ('A'+control.channel[p_track].current_command) {
|
|
|
|
case 'A': {
|
|
|
|
if ((control.ticks_counter>0) || (control.pattern_delay_2>0)) break;
|
|
|
|
int new_speed;
|
|
|
|
new_speed=control.channel[p_track].current_parameter % 128;
|
|
|
|
if (new_speed>0) {
|
|
control.speed=new_speed;
|
|
control.ticks_counter=0;
|
|
}
|
|
} break;
|
|
case 'B': {
|
|
|
|
int next_order;
|
|
|
|
if (control.ticks_counter || control.position.forbid_jump) break;
|
|
|
|
control.position.current_row=0;
|
|
|
|
if (control.play_mode==PLAY_PATTERN) break;
|
|
|
|
next_order=get_song_next_order_idx(song, (int)control.channel[p_track].current_parameter-1);
|
|
|
|
if (next_order!=-1) {
|
|
// Do we have a "next order?"
|
|
control.position.current_pattern=song->get_order(next_order);
|
|
control.position.force_next_order=next_order;
|
|
|
|
} else {
|
|
// no, probably the user deleted the orderlist.
|
|
control.play_mode=PLAY_NOTHING;
|
|
reset();
|
|
}
|
|
} break;
|
|
case 'C': {
|
|
|
|
int next_order;
|
|
|
|
if (control.ticks_counter || control.position.forbid_jump) break;
|
|
|
|
control.position.current_row=control.channel[p_track].current_parameter;
|
|
|
|
if (control.play_mode==PLAY_PATTERN) {
|
|
|
|
if (control.position.current_row>=song->get_pattern(control.position.current_pattern)->get_length()) {
|
|
|
|
control.position.current_row=0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
next_order=get_song_next_order_idx(song, (int)control.position.current_order);
|
|
|
|
if (next_order!=-1) {
|
|
// Do we have a "next order?"
|
|
control.position.current_pattern=song->get_order(next_order);
|
|
|
|
if (control.position.current_row>=song->get_pattern(song->get_order(next_order))->get_length()) {
|
|
|
|
control.position.current_row=0;
|
|
}
|
|
|
|
control.position.force_next_order=next_order;
|
|
|
|
} else {
|
|
// no, probably the user deleted the orderlist.
|
|
control.play_mode=PLAY_NOTHING;
|
|
reset();
|
|
}
|
|
|
|
} break;
|
|
case 'D': {
|
|
|
|
uint8_t inf ;
|
|
//explicitslides=1;
|
|
inf=control.channel[p_track].current_parameter;
|
|
|
|
if (inf) control.channel[p_track].volume_slide_info=inf;
|
|
else inf=control.channel[p_track].volume_slide_info;
|
|
|
|
do_volume_slide(p_track,inf);
|
|
|
|
} break;
|
|
case 'E': {
|
|
|
|
uint8_t inf;
|
|
|
|
inf=control.channel[p_track].current_parameter;
|
|
do_pitch_slide_down(p_track,inf);
|
|
|
|
} break;
|
|
case 'F': {
|
|
|
|
uint8_t inf;
|
|
|
|
inf=control.channel[p_track].current_parameter;
|
|
do_pitch_slide_up(p_track,inf);
|
|
|
|
} break;
|
|
case 'G': {
|
|
|
|
if (control.channel[p_track].current_parameter) {
|
|
|
|
control.channel[p_track].portamento_speed=control.channel[p_track].current_parameter;
|
|
}
|
|
|
|
if (control.channel[p_track].period && (control.channel[p_track].old_note<=120)) {
|
|
|
|
if ( (!control.ticks_counter) && (control.channel[p_track].new_instrument) ){
|
|
|
|
|
|
control.channel[p_track].kick=KICK_NOTE;
|
|
control.channel[p_track].sample_start_index=0;
|
|
|
|
} else {
|
|
|
|
control.channel[p_track].kick=(control.channel[p_track].kick==KICK_NOTE)?KICK_ENVELOPE:KICK_NOTHING;
|
|
}
|
|
|
|
do_pitch_slide_to_note(p_track);
|
|
control.channel[p_track].has_own_period=true;
|
|
}
|
|
|
|
} break;
|
|
case 'H': {
|
|
|
|
uint8_t dat;
|
|
|
|
control.channel[p_track].doing_vibrato=true;
|
|
|
|
dat=control.channel[p_track].current_parameter;
|
|
|
|
if (!control.ticks_counter) {
|
|
if (dat&0x0f) control.channel[p_track].vibrato_depth=dat&0xf;
|
|
if (dat&0xf0) control.channel[p_track].vibrato_speed=(dat&0xf0)>>2;
|
|
}
|
|
|
|
if (control.external_vibrato) break;
|
|
|
|
if (control.channel[p_track].period) {
|
|
|
|
do_vibrato(p_track,false);
|
|
control.channel[p_track].has_own_period=true;
|
|
}
|
|
|
|
} break;
|
|
case 'I': {
|
|
|
|
do_tremor(p_track);
|
|
control.channel[p_track].has_own_volume=true;
|
|
} break;
|
|
case 'J': {
|
|
|
|
do_arpegio(p_track);
|
|
} break;
|
|
case 'K': {
|
|
|
|
uint8_t inf ;
|
|
//explicitslides=1;
|
|
inf=control.channel[p_track].current_parameter;
|
|
|
|
control.channel[p_track].doing_vibrato=true;
|
|
|
|
|
|
if (inf) control.channel[p_track].volume_slide_info=inf;
|
|
else inf=control.channel[p_track].volume_slide_info;
|
|
|
|
do_volume_slide(p_track,inf);
|
|
|
|
if (control.external_vibrato) break;
|
|
|
|
if (control.channel[p_track].period) {
|
|
|
|
do_vibrato(p_track,false);
|
|
control.channel[p_track].has_own_period=true;
|
|
}
|
|
|
|
} break;
|
|
case 'L': {
|
|
uint8_t inf ;
|
|
//explicitslides=1;
|
|
inf=control.channel[p_track].current_parameter;
|
|
|
|
if (inf) control.channel[p_track].volume_slide_info=inf;
|
|
else inf=control.channel[p_track].volume_slide_info;
|
|
|
|
do_volume_slide(p_track,inf);
|
|
|
|
if (control.channel[p_track].period && (control.channel[p_track].old_note<=120)) {
|
|
if ( (!control.ticks_counter) && (control.channel[p_track].new_instrument) ){
|
|
|
|
control.channel[p_track].kick=KICK_NOTE;
|
|
control.channel[p_track].sample_start_index=0;
|
|
|
|
} else {
|
|
|
|
control.channel[p_track].kick=(control.channel[p_track].kick==KICK_NOTE)?KICK_ENVELOPE:KICK_NOTHING;
|
|
}
|
|
|
|
do_pitch_slide_to_note(p_track);
|
|
control.channel[p_track].has_own_period=true;
|
|
}
|
|
} break;
|
|
case 'M': {
|
|
control.channel[p_track].channel_volume=control.channel[p_track].current_parameter;
|
|
if (control.channel[p_track].channel_volume>64) control.channel[p_track].channel_volume=64;
|
|
else if (control.channel[p_track].channel_volume<0) control.channel[p_track].channel_volume=0;
|
|
} break;
|
|
case 'N': {
|
|
|
|
do_channel_volume_slide(p_track);
|
|
}
|
|
case 'O': {
|
|
|
|
if (!control.ticks_counter) {
|
|
|
|
if (control.channel[p_track].current_parameter) control.channel[p_track].lo_offset=(uint16_t)control.channel[p_track].current_parameter<<8;
|
|
control.channel[p_track].sample_start_index=control.channel[p_track].hi_offset|control.channel[p_track].lo_offset;
|
|
|
|
//if ((control.channel[p_track].sample_ptr!=NULL)&&(control.channel[p_track].sample_start_index>control.channel[p_track].sample_ptr->data.size)) {
|
|
//TODO, O effect
|
|
//a->start=a->s->flags&(SF_LOOP|SF_BIDI)?a->s->loopstart:a->s->length;
|
|
//}
|
|
}
|
|
} break;
|
|
case 'P': {
|
|
|
|
do_pan_slide(p_track);
|
|
} break;
|
|
case 'Q': {
|
|
do_retrig(p_track);
|
|
|
|
} break;
|
|
case 'R': {
|
|
|
|
|
|
uint8_t dat;
|
|
|
|
if (control.channel[p_track].current_parameter) {
|
|
|
|
control.channel[p_track].tremolo_info=control.channel[p_track].current_parameter;
|
|
}
|
|
|
|
dat=control.channel[p_track].tremolo_info;
|
|
|
|
if (!control.ticks_counter && dat) {
|
|
|
|
if (dat&0x0f) control.channel[p_track].tremolo_depth=dat&0xf;
|
|
if (dat&0xf0) control.channel[p_track].tremolo_speed=(dat&0xf0)>>2;
|
|
}
|
|
|
|
do_tremolo(p_track);
|
|
control.channel[p_track].has_own_volume=true;
|
|
|
|
} break;
|
|
case 'S': {
|
|
|
|
do_effect_S(p_track);
|
|
} break;
|
|
case 'T': {
|
|
uint8_t dat;
|
|
int16_t temp=control.tempo;
|
|
|
|
if (control.pattern_delay_2) return;
|
|
|
|
if (control.channel[p_track].current_parameter) {
|
|
|
|
control.channel[p_track].tempo_slide_info=control.channel[p_track].current_parameter;
|
|
}
|
|
|
|
dat=control.channel[p_track].tempo_slide_info;
|
|
|
|
if (dat>=0x20) {
|
|
|
|
if (control.ticks_counter) break;
|
|
control.tempo=dat;
|
|
} else {
|
|
|
|
if (!control.ticks_counter) break;
|
|
|
|
if (dat&0x10) {
|
|
|
|
temp+=(dat&0x0f);
|
|
} else {
|
|
|
|
temp-=dat;
|
|
}
|
|
control.tempo=(temp>255)?255:(temp<0x20?0x20:temp);
|
|
}
|
|
|
|
} break;
|
|
case 'U': {
|
|
|
|
uint8_t dat;
|
|
|
|
dat=control.channel[p_track].current_parameter;
|
|
control.channel[p_track].doing_vibrato=true;
|
|
if (!control.ticks_counter) {
|
|
if (dat&0x0f) control.channel[p_track].vibrato_depth=dat&0xf;
|
|
if (dat&0xf0) control.channel[p_track].vibrato_speed=(dat&0xf0)>>2;
|
|
}
|
|
|
|
if (control.external_vibrato) break;
|
|
|
|
if (control.channel[p_track].period) {
|
|
|
|
do_vibrato(p_track,true);
|
|
control.channel[p_track].has_own_period=true;
|
|
}
|
|
} break;
|
|
case 'V': {
|
|
|
|
control.global_volume=control.channel[p_track].current_parameter;
|
|
if (control.global_volume>128) control.global_volume=128;
|
|
} break;
|
|
case 'W': {
|
|
do_global_volume_slide(p_track);
|
|
} break;
|
|
case 'X': {
|
|
//sets both channel and current
|
|
control.channel[p_track].channel_panning=control.channel[p_track].current_parameter;
|
|
control.channel[p_track].panning=control.channel[p_track].current_parameter;
|
|
} break;
|
|
case 'Y': {
|
|
|
|
uint8_t dat;
|
|
|
|
if (control.channel[p_track].current_parameter) {
|
|
|
|
control.channel[p_track].panbrello_info=control.channel[p_track].current_parameter;
|
|
}
|
|
|
|
dat=control.channel[p_track].panbrello_info;
|
|
|
|
if (!control.ticks_counter) {
|
|
|
|
if (dat&0x0f) control.channel[p_track].panbrello_depth=(dat&0xf);
|
|
if (dat&0xf0) control.channel[p_track].panbrello_speed=(dat&0xf0)>>4;
|
|
}
|
|
|
|
//if (pf->panflag)
|
|
if (control.channel[p_track].panning!=PAN_SURROUND)do_panbrello(p_track);
|
|
|
|
} break;
|
|
case 'Z': {
|
|
//I DO! cuttoff!
|
|
uint16_t dat=control.channel[p_track].current_parameter;
|
|
|
|
if (dat<0x80) {
|
|
|
|
control.channel[p_track].filter.it_cutoff=dat*2;
|
|
if (control.channel[p_track].filter.it_cutoff>0x80)
|
|
control.channel[p_track].filter.it_cutoff++;
|
|
} else if (dat<0x90) {
|
|
|
|
control.channel[p_track].filter.it_reso=(dat-0x80)*0x10;
|
|
} else {
|
|
|
|
control.channel[p_track].reverb_send=(dat-0x90)*255/0x6F;
|
|
}
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void CPPlayer::pre_process_effects() {
|
|
|
|
// MP_VOICE *aout;
|
|
int i;
|
|
|
|
for (i=0;i<CPPattern::WIDTH;i++) {
|
|
|
|
//a=&pf->control[mp_channel];
|
|
|
|
// if ((aout=a->slave)) {
|
|
// a->fadevol=aout->fadevol;
|
|
// a->period=aout->period;
|
|
// if (a->kick==KICK_KEYOFF) a->keyoff=aout->keyoff;
|
|
//}
|
|
|
|
//if (!a->row) continue;
|
|
//UniSetRow(a->row);
|
|
control.channel[i].has_own_period=false;
|
|
control.channel[i].has_own_volume=false;
|
|
control.channel[i].doing_vibrato=false;
|
|
//explicitslides=0;
|
|
//pt_playeffects();
|
|
if (control.ticks_counter<control.speed) {
|
|
|
|
run_effects(i);
|
|
run_volume_column_effects(i);
|
|
}
|
|
|
|
/* continue volume slide if necessary for XM and IT */
|
|
//if (pf->flags&UF_BGSLIDES) {
|
|
// if (!explicitslides && a->sliding)
|
|
// DoS3MVolSlide(0);
|
|
// else if (a->tmpvolume) a->sliding=explicitslides;
|
|
//}
|
|
|
|
if (!control.channel[i].has_own_period) control.channel[i].period=control.channel[i].aux_period;
|
|
if (!control.channel[i].has_own_volume) control.channel[i].volume=control.channel[i].aux_volume;
|
|
|
|
if ((control.channel[i].sample_ptr!=NULL) && !(song->has_instruments() && (control.channel[i].instrument_ptr==NULL))) {
|
|
|
|
if (song->has_instruments()) {
|
|
|
|
control.channel[i].output_volume=
|
|
(control.channel[i].volume*control.channel[i].sample_ptr->get_global_volume()*control.channel[i].instrument_ptr->get_volume_global_amount())/2048;
|
|
control.channel[i].output_volume=control.channel[i].output_volume*control.channel[i].random_volume_variation/100;
|
|
|
|
} else {
|
|
|
|
control.channel[i].output_volume=
|
|
(control.channel[i].volume*control.channel[i].sample_ptr->get_global_volume())>>4;
|
|
|
|
}
|
|
|
|
if (control.channel[i].output_volume>256) {
|
|
|
|
control.channel[i].output_volume=256;
|
|
|
|
} else if (control.channel[i].output_volume<0) {
|
|
|
|
control.channel[i].output_volume=0;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
}
|