/*
    PsyTexx: arm_mod.c. ARM MOD player.
    Copyright (C) 2004  Zolotov Alexandr

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
//*** Contact info: Zolotov Alexandr (NightRadio project)
//***               Ekaterinburg. Russia.
//***               Email: observer_page@mail.ru
//***                      warmplace@warmplace.ru
//***                      nightradio@knoppix.ru
//***               WWW: warmplace.ru

//#define RECORD_TO_FILE
//#define ECHO_TYPE_NEW
#define ECHO_TYPE_OLD
#define INTERPOLATION
#define SPEC_EFFECTS_ON
#ifdef WIN32
#	include "PceNativeCall.h"
#	include "SimNative.h"
#	include "e_utils.h"
#	include "modplay.h"
#	include "stdio.h"
#else
#	include "PceNativeCall.h"
#	include "Standalone.h"
#	include "e_utils.h"
#	include "modplay.h"
STANDALONE_CODE_RESOURCE_ID (1000);
#endif


typedef struct {
   long file_is_open;
#ifdef WIN32
   FILE* out_file;
#else
   long out_file;
#endif

   char* resonance_buffer; //256 bytes

   void* record_buf;
   long record_pos;

   void* echo_buffer;
   long echo_size;
   long echo_pointer;
   long echo_flange_delay;
   long echo_vol;

   void* reverb_offset;
   long reverb_number;

   long win32_frameCount;
   void* win32_buffer;

   char *rec_pointer;
   long rec_count;
   long rec_add;

   char *_arm_code;
   channel *_channels;
   signed long _patternticks;
   signed long _onetick;
   ulong _sp;
   ulong _speed;
   ulong _bpm;
   note **_patterndata2;
   note **_patterndata;
   module *_song2;
   module *_song;
   signed char *_buffer;
   channel *_one;
   uint *_fine2;
   uint *_fine;
   ulong _sampleticksconst;
   char *_pp;
   signed char **_sampledata2;
   signed char **_sampledata;
   signed long i2, size;
   ulong _update;
   uint _tablepos;
   uint _patternpos;
   uint _num_of_mods;
   uint _playrate;
   uint _buffer_size;
   uint CHANNELS;
   uint VOLUME;
   signed char _scope[60];
   signed char _status;
} user_info;

#ifdef WIN32
	//unsigned long ARMlet_Main(const void* emulStateP, void *userData, Call68KFuncType *call68KFuncP);
#else
	int main_callback(void*,long,void*,long);
#endif

//Other functions:
void convert_all(user_info*);
void convert_channel(channel*);
void effect(channel *, user_info *);
void worknote(note *, channel *, user_info *);


//Effect macroses:
#define VOLUME_CONTROL(l) \
	if(l<-32765) l=-32765; \
	if(l>32765) l=32675; 

//l,l2 >> 5 = 0..16k
//vol = 0..31
#define echo_accumulate( l, l2, vol ) \
if( vol ) { \
	/*calculate current value from stereo values*/ \
	echo_res2 = (l+l2) >> 6; \
	echo_res += (echo_res2 * vol) >> 5; \
}

#define echo_reset() echo_res = 0;

#define echo_send() \
if( echo_buffer) { \
	/*put current value to the echo-buffer*/ \
	echo_p1 = echo_pointer - 34; \
	if( echo_p1 < 0 ) echo_p1 += echo_size; \
	if( !(echo_p1&1) ) { \
		echo_res += echo_buffer[ echo_p1>>1 ]; \
		VOLUME_CONTROL( echo_res ); \
		echo_buffer[ echo_p1>>1 ] = (signed short)echo_res; \
	} \
}

#define reverb_send( l, l2, vol ) \
if( vol ) { \
	/*calculate current value from stereo values*/ \
	echo_res = (l+l2) >> 6; \
	echo_res = (echo_res * vol) >> 6; \
	/*put current value to the echo-buffer*/ \
	for( reverb_cur = 0; reverb_cur < reverb_number; reverb_cur++ ) \
	{ \
		echo_p1 = echo_pointer + ((long)reverb_offset[reverb_cur]<<8); \
		if( echo_p1 < 0 ) { echo_p1 += echo_size; \
			if( echo_p1 < 0 ) echo_p1 += echo_size; \
		} \
		if( echo_p1 >= echo_size ) { echo_p1 -= echo_size; \
			if( echo_p1 >= echo_size ) echo_p1 -= echo_size; \
		} \
		if( !(echo_p1&1) ) { \
			echo_res2 = echo_res + echo_buffer[ echo_p1>>1 ]; \
			VOLUME_CONTROL( echo_res2 ); \
			echo_buffer[ echo_p1>>1 ] = echo_res2; \
		} \
	} \
}

#ifdef ECHO_TYPE_OLD
#define echo_get( l, l2 ) \
if( echo_buffer ) { \
	/*blur old value*/ \
	if( !(echo_pointer&1) ) { \
		echo_p1 = echo_pointer + 2; if( echo_p1 >= echo_size ) echo_p1 -= echo_size; \
		echo_res = (long)echo_buffer[ echo_pointer>>1 ] + (long)echo_buffer[ echo_p1>>1 ]; \
		echo_res = (echo_res * echo_vol) >> 8; \
		VOLUME_CONTROL( echo_res ); \
		echo_buffer[ echo_pointer>>1 ] = (signed short)echo_res; \
	} \
	/*get old values*/ \
	echo_p2 = echo_pointer - 32; if( echo_p2 < 0 ) echo_p2 += echo_size; \
	l += (long)echo_buffer[ echo_pointer>>1 ] << 1; \
	l2 += (long)echo_buffer[ echo_p2>>1 ] << 1; \
	echo_pointer ++; \
	/*check for the echo bounds*/ \
	if( echo_pointer >= echo_size ) echo_pointer -= echo_size; \
}
#endif

#ifdef ECHO_TYPE_NEW
#define echo_get( l, l2 ) \
if( echo_buffer ) { \
	/*blur old value*/ \
	if( !(echo_pointer&1) ) { \
		echo_p1 = echo_pointer + 2; if( echo_p1 >= echo_size ) echo_p1 -= echo_size; \
		echo_res = (long)echo_buffer[ echo_pointer>>1 ] + (long)echo_buffer[ echo_p1>>1 ]; \
		echo_res = (echo_res * echo_vol) >> 8; \
		VOLUME_CONTROL( echo_res ); \
		echo_buffer[ echo_pointer>>1 ] = (signed short)echo_res; \
	} \
	/*get old values*/ \
	echo_p2 = echo_pointer - 32; if( echo_p2 < 0 ) echo_p2 += echo_size; \
	l += (long)echo_buffer[ echo_pointer>>1 ] << 1; \
	l2 += (long)echo_buffer[ echo_p2>>1 ] << 1; \
	echo_pointer ++; \
	/*check for the echo bounds*/ \
	if( echo_pointer >= echo_size ) echo_pointer -= echo_size; \
}
#endif

#ifdef SPEC_EFFECTS_ON
#define RESET_SPEC_EFFECTS \
if( spec_effects = cptr->spec_effects ) \
{ \
	cptr->dist = 0; \
}
#define SPEC_EFFECTS( v ) \
if( spec_effects = cptr->spec_effects ) \
{ \
	if( (spec_effects & SF_CUTOFF) ) \
	{ \
		if( (spec_effects & SF_WAHWAH) ) \
		{\
			cptr->wah_offset += cptr->wah_add;\
			if( cptr->wah_offset < (1<<16) ) cptr->wah_add = cptr->wah_amp;\
			if( cptr->wah_offset > (255<<16) ) cptr->wah_add = -cptr->wah_amp;\
			cutoff = cptr->wah_offset>>16;\
		}\
		else cutoff = cptr->cutoff;\
		click_delta = cptr->cold_volume - (v<<8);\
		v = cptr->cold_volume - ((click_delta * cutoff)>>8);\
		cptr->cold_volume = v;\
		if( (spec_effects & SF_RESONANCE) ) \
		{\
			cptr->res_ptr += cptr->res_delta;\
			resonance_wave = U->resonance_buffer[ (cptr->res_ptr>>16) & 255 ] >> cptr->res_volume;\
			v += ( resonance_wave * click_delta ) >> 7;\
		}\
		v >>= 8;\
	} \
	if( (spec_effects & SF_ANTICLICK) ) \
	{ \
		click_delta = cptr->old_volume - v;\
		if( click_delta < 0 ) click_delta = -click_delta;\
		if( click_delta > 32 ) v = ( cptr->old_volume + v ) >> 1;\
		cptr->old_volume = v;\
	} \
	if( (spec_effects & SF_SPORTAMENTO) ) \
	{ \
		if( (long)cptr->delta < cptr->new_delta ) \
		{\
			cptr->delta += cptr->ddelta;\
			if( (long)cptr->delta > cptr->new_delta ) cptr->delta = cptr->new_delta;\
		}\
		if( (long)cptr->delta > cptr->new_delta ) \
		{\
			cptr->delta -= cptr->ddelta;\
			if( (long)cptr->delta < cptr->new_delta ) cptr->delta = cptr->new_delta;\
		}\
	} \
	if( (spec_effects & SF_DISTORTION) ) \
	{ \
		cptr->dist += v;\
		if( cptr->dist > 64 ) cptr->dist = 64;\
		if( cptr->dist < -64 ) cptr->dist = -64;\
		v = cptr->dist;\
	} \
}
#else
#define SPEC_EFFECTS( v ) /*none*/ 
#define RESET_SPEC_EFFECTS /*none*/
#endif

#define ANTICLICK( v ) \
/* ANTICLICK FILTER : */\
if( cptr->anticlick > 0 ) \
{ \
	v = ( (v * (255-cptr->anticlick)) + (cptr->anticlick_start * cptr->anticlick) ) >> 8; \
	cptr->anticlick -= 4; \
}

#define START_ANTICLICK cptr->anticlick = 255; cptr->anticlick_start = cptr->previous_value;


//Main function:
#ifdef WIN32
unsigned long ARMlet_Main(const void* emulStateP, void *userData, Call68KFuncType *call68KFuncP)
#else
int main_callback(void *userData,
                  long stream,
                  void *_buffer,
                  long frameCount)
#endif
{
        long    j;
		ulong   k;
        long    i, l, l2, l3, i2, size, ostalos;
		long    int1, int2; //for interpolation
        note    *nptr;
        channel *cptr;
        int yy=0;
        note **_patterndata;
        module *_song;
        uchar *_patterntable;
        uint buffer_size;
        signed short *buffer;
		signed char *rec_buffer;
		long rec_count;
		long rec_res;
		long rec_add;
        signed char *scope;
		uint CHANNELS;
		uint VOLUME;
		ulong cc; //cur_channel

		signed short *echo_buffer;
		long echo_size;
		long echo_pointer;
		long echo_flange_delay;
		long echo_vol;
		long echo_p1;
		long echo_p2;
		long echo_res;
		long echo_res2;
		signed char* reverb_offset;
		long reverb_number;
//		long reverb_cur;
		long lval;
		long rval;

		long spec_effects;
		long click_delta;
		long resonance_wave;
		long cutoff;

		signed char* record_buf;

  user_info *U;
  U=userData;
		record_buf = (signed char*)U->record_buf;
		if( U->record_pos < 60000 ) { //RECORDING FROM MICROPHONE:
#ifndef WIN32
			buffer_size=frameCount;
			buffer=_buffer;
#else
			buffer_size=(unsigned short)U->win32_frameCount;
			buffer=U->win32_buffer;
#endif
			for (i = 0; i < buffer_size; i++) {
				if( U->record_pos >= 60000 ) break;
				l = buffer[i];l >>= 8;
				record_buf[ U->record_pos ] = (signed char)l;
				U->record_pos ++;
			}
			return 0;
		}
  convert_all(U);

        _patterndata=U->_patterndata;
        _song=U->_song;
        _patterntable=_song->patterntable;
#ifdef WIN32
        buffer_size=(unsigned short)U->win32_frameCount;
		buffer=U->win32_buffer;
#else
        buffer_size=frameCount;
		buffer=_buffer;
#endif
		echo_buffer = U->echo_buffer;
		echo_size = U->echo_size * (U->_onetick>>8);
		echo_pointer = U->echo_pointer;
		echo_flange_delay = U->echo_flange_delay;
		echo_vol = U->echo_vol;
		reverb_offset = U->reverb_offset;
		reverb_number = U->reverb_number;
        scope=U->_scope;
		CHANNELS = U->CHANNELS;
		VOLUME = U->VOLUME;

if(U->_num_of_mods!=0){
if(U->_status>=2){

        for(i=0;i<buffer_size;i++) buffer[i]=0;
        cptr = U->_channels;
        i2=0;ostalos=buffer_size;
new_tick:
        size=(U->_onetick - U->_patternticks)/256;size++;
        if(size<0) size=0;
        if(size>ostalos) size=ostalos;
        for(i=(i2<<1);i<((i2+size)<<1);i+=2){
				echo_reset();
				RESET_SPEC_EFFECTS;
                for (j = l = l2 = 0, cptr; j < CHANNELS; j++, cptr++) {
						lval = rval = 0;
						if( !cptr->sampdata ) continue;
					//Pitch: =======
					if( cptr->pitch )
					{
						cptr->pitch_ticks += cptr->pitch;
						k = cptr->pitch_ticks >> 16;
						if (k >= (ulong)cptr->rep) {
							if(cptr->replen>2){
								cptr->pitch_ticks = 
									cptr->pitch_ticks - ((ulong)(cptr->replen)<<16);
							}else{
								cptr->pitch_ticks = (ulong)(cptr->length)<<16;
								cptr->sampdata = 0;
							}
						}
					}
					//==============
                        cptr->ticks += cptr->delta;
                        k=cptr->ticks>>16;
#ifdef INTERPOLATION
						int1 = cptr->ticks & 0xFFFF; int2 = 0xFFFF - int1;
#endif
next_iteration:
                        if (k >= cptr->rep) {
                           if(cptr->replen>2){
                             cptr->ticks = cptr->ticks - ((ulong)(cptr->replen)<<16);
                             k=cptr->ticks>>16;
							 if( k >= cptr->rep ) goto next_iteration;
#ifdef INTERPOLATION
							 lval = (cptr->sampdata[k]*int2)>>16;
							 lval += (cptr->sampdata[k+1]*int1)>>16;
#else 
							 lval = cptr->sampdata[k];
#endif
							 SPEC_EFFECTS( lval );
							 lval *= cptr->volume;
							 ANTICLICK( lval );
							 cptr->previous_value = lval;
							 rval = lval;
							 lval = lval * cptr->global_volume_l;
							 rval = rval * cptr->global_volume_r;
                           }else{
                             cptr->ticks = (ulong)(cptr->length)<<16;
							 if( !cptr->pitch ) cptr->sampdata = 0;
							 cptr->previous_value = 0;
                           }
                        }else{
#ifdef INTERPOLATION
							lval = (cptr->sampdata[k]*int2)>>16;
							if(k == (ulong)cptr->rep - 1) { //right sample bound:
								if( cptr->replen > 2 ) { //if loop
									k = cptr->reppnt; 
									lval += (cptr->sampdata[k]*int1)>>16;
								}
							} else lval += (cptr->sampdata[k+1]*int1)>>16; //just sample playing
#else 
							lval = cptr->sampdata[k];
#endif
							SPEC_EFFECTS( lval );
							lval *= cptr->volume;
							ANTICLICK( lval );
							cptr->previous_value = lval;
							rval = lval;
							lval = lval * cptr->global_volume_l;
							rval = rval * cptr->global_volume_r;
                        }

                        l += lval;
                        l2 += rval;
						l3=l>>14;if(l3<0) l3=-l3; cptr->summar_volume = (unsigned short)l3;
						
						//ECHO PROCESSING:
						echo_accumulate( lval, rval, cptr->echo_volume );
						//===============
                }
				//ECHO PROCESSING:
				echo_send();
				//===============
				l>>=4;
				l2>>=4;
				//ECHO PROCESSING:
				echo_get( l, l2 );
				//===============
                l*=VOLUME; //volume = 0..256
				l2*=VOLUME;
				l>>=8;
				l2>>=8;
				VOLUME_CONTROL(l);
				VOLUME_CONTROL(l2);
				buffer[i] = (signed short)l;
                buffer[i+1] = (signed short)l2;
                cptr-=CHANNELS;
        }
        U->_patternticks+=(size*256);
                if (U->_patternticks > U->_onetick) {
                        U->_patternticks-=U->_onetick;
                   if (U->_sp-- == 1) { U->_sp=U->_speed;
                        nptr = _patterndata[_patterntable[U->_tablepos]];
                        nptr = nptr + U->_patternpos;
                        U->_patternpos += CHANNELS;

                        for(cc=0;cc<CHANNELS;cc++) worknote(nptr+cc, cptr+cc, U);
                        
                        if (U->_patternpos == CHANNELS*64) {
                                if(U->_status!=3){
                                  U->_tablepos++;
                                  if(U->_tablepos>=_song->length){U->_tablepos=0;}
                                }
                                U->_patternpos = 0;
                        }
                   }

                   for(cc=0;cc<CHANNELS;cc++) effect(cptr+cc, U);
                }
        i2+=size;
        if(i2<buffer_size) {ostalos=buffer_size-i2; goto new_tick;}
        goto end_mod;
}
if(U->_status==1){
        convert_channel(U->_one);
                        cptr = U->_one;
        for (i = 0; i < (buffer_size<<1); i+=2) {

						l = l2 = 0;
						echo_reset();
						RESET_SPEC_EFFECTS;
                        cptr->ticks += cptr->delta;
                        k=cptr->ticks>>16;
						int1 = cptr->ticks & 0xFFFF; int2 = 0xFFFF - int1;
                        if (k >= (ulong)cptr->rep) {
                           if(cptr->replen>2){
                             cptr->ticks = cptr->ticks - ((ulong)(cptr->replen)<<16);
                             k=cptr->ticks>>16;
							 l = (cptr->sampdata[k]*int2)>>16;
							 l += (cptr->sampdata[k+1]*int1)>>16;
							 SPEC_EFFECTS( l );
							 l2 = l;
							 l = l * cptr->volume * 64;
							 l2 = l2 * cptr->volume * 64;
                           }else{
                             cptr->ticks = (ulong)(cptr->length)<<16;
							 cptr->sampdata = 0;
							 l = 0;
							 l2 = 0;
                           }
                        }else{
							 l = (cptr->sampdata[k]*int2)>>16;
							 if(k == (ulong)cptr->rep - 1) { //right sample bound:
							 	 if( cptr->replen > 2 ) { //if loop
									 k = cptr->reppnt; 
									 l += (cptr->sampdata[k]*int1)>>16;
								 }
							 } else l += (cptr->sampdata[k+1]*int1)>>16; //just sample playing
							 SPEC_EFFECTS( l );
							 l2 = l;
						     l = l * cptr->volume * 64;
						     l2 = l2 * cptr->volume * 64;
                        }
						//ECHO PROCESSING:
						echo_accumulate( l, l2, cptr->echo_volume );
						echo_send();
						//===============
						l>>=4;
						l2>>=4;
						//ECHO PROCESSING:
						echo_get( l, l2 );
						//===============
						l*=VOLUME; //volume = 0..256
						l2*=VOLUME;
						l>>=8;
						l2>>=8;
						VOLUME_CONTROL(l);
						VOLUME_CONTROL(l2);
						buffer[i] = (signed short)l;
						buffer[i+1] = (signed short)l2;
        }
        convert_channel(U->_one);
        goto end_mod;
}}
if(U->_status==0){
        for (i = 0; i < (buffer_size<<1); i+=2) {
			l = 0; l2 = 0;
			//ECHO PROCESSING:
			echo_get( l, l2 );
			//===============
            l*=VOLUME; //volume = 0..256
			l2*=VOLUME;
			l>>=8;
			l2>>=8;
			VOLUME_CONTROL(l);
			VOLUME_CONTROL(l2);
			buffer[i]=(signed short)l;buffer[i+1]=(signed short)l2;
		}
}
  end_mod:
  
//Record to the COPY buffer:
	if( U->rec_pointer != 0 && U->_status >= 2 ) {
		rec_buffer = (signed char*)ByteSwap32( U->rec_pointer );
		rec_count = ByteSwap32( U->rec_count );
		rec_add = ByteSwap32( U->rec_add );
		for( i = 0; i < (buffer_size<<1); i+= (rec_add<<1) ) {
			if( rec_count > 60000 ) { U->rec_pointer = 0; break; }
			rec_res = buffer[i] >> 9;
			rec_res += buffer[i+1] >> 9;
			rec_buffer[ rec_count ] = (signed char)rec_res;
			rec_count ++;
		}
		U->rec_count = ByteSwap32( rec_count );
	}
#ifdef WIN32
#ifdef RECORD_TO_FILE
	if( U->_status >= 2 ) {
		U->out_file = fopen( "out_file.bin", "ab" );
		fwrite( buffer, buffer_size, 4, U->out_file );
		fclose( U->out_file );
	}
#endif
#endif
//=========================

	U->echo_pointer = echo_pointer;
	U->echo_flange_delay = echo_flange_delay;

  U->_update=1;
  //for(j=0;j<60;j++){scope[j]=buffer[j];}
  convert_all(U);
  return 0;
}

void worknote(note *nptr, channel *cptr, user_info *U)
{
        uint    sample, period, effect;

        module *_song=U->_song;
        char *_pp=U->_pp;
        signed char **_sampledata=U->_sampledata;
        uint *_fine=U->_fine;

		uchar *s_info;
		int a;

        sample = (nptr->sampperiod & 0xF0) | (nptr->sampeffect >> 4);
        period = ((nptr->sampperiod & 0xF) << 8) | nptr->period;
        effect = ((nptr->sampeffect & 0xF) << 8) | nptr->effect;
        if (sample != 0) {
                if (sample != 0) cptr->sampnum = --sample;

				//Set spec-effects:=======
				s_info = _song->samples[cptr->sampnum]->name;
				cptr->spec_effects = 0;
				if( s_info[ 0 ] == '@' )
				{
					for(a=1;a<20;a++)
					{
						if( s_info[ a ] == '@' ) break;
						switch( s_info[ a ] )
						{
						case 'A':
							cptr->spec_effects |= SF_ANTICLICK;
							break;
						case 'S':
							cptr->spec_effects |= SF_SPORTAMENTO;
							break;
						case 'D':
							cptr->spec_effects |= SF_DISTORTION;
							break;
						case 'C':
							cptr->spec_effects |= SF_CUTOFF;
							break;
						case 'R':
							cptr->spec_effects |= SF_RESONANCE;
							break;
						case 'W':
							cptr->spec_effects |= SF_WAHWAH;
							break;
						}
					}
				}
				//========================
				cptr->echo_volume = s_info[21]; //Echo volume;
				if(cptr->echo_volume > 32) cptr->echo_volume = 0;            //Correct echo volume

                if(period) 
				{
					cptr->new_period=_fine[((_song->samples[cptr->sampnum]->finetune*60)+_pp[period])];
				}
        if (effect>>8!=3){
				cptr->ddelta = 0;
                cptr->sampdata = _sampledata[cptr->sampnum];
                cptr->length = _song->samples[cptr->sampnum]->length;
                cptr->reppnt = _song->samples[cptr->sampnum]->reppnt;
                cptr->replen = _song->samples[cptr->sampnum]->replen;
                cptr->volume = _song->samples[cptr->sampnum]->volume;
                if(period){  
				  period=_fine[((_song->samples[cptr->sampnum]->finetune*60)+_pp[period])];
                  cptr->new_delta = cptr->delta = U->_sampleticksconst/period;
                  cptr->period = period;
                  cptr->ticks = 0;
				  cptr->wah_offset = 0;
				  cptr->cutoff = 255;
				  START_ANTICLICK;
				}
                  if(cptr->replen>2){cptr->rep = cptr->reppnt+cptr->replen;}
                                else{cptr->rep = cptr->length;}
        }}
                cptr->p_delta=0;
				cptr->v_up=0;
				cptr->v_down=0;
		if( (effect >> 8) != 0x8 ) cptr->pitch = 0;
        switch (effect >> 8) {
                case 0x1: if((int)(effect&0xFF)==0){
                            cptr->p_delta=cptr->p_olddelta;
                          }else{cptr->p_delta=-(int)(effect&0xFF);}
                          cptr->p_up=1814;
                          cptr->p_down=54;
                          cptr->p_olddelta=cptr->p_delta;

						  cptr->new_delta = 0xFFFFFFF;
						  cptr->ddelta = -cptr->p_olddelta;
                          break;
                case 0x2: if((int)(effect&0xFF)==0){
                            cptr->p_delta=cptr->p_olddelta;
                          }else{cptr->p_delta=(int)(effect&0xFF);}
                          cptr->p_up=1814;
                          cptr->p_down=54;
                          cptr->p_olddelta=cptr->p_delta;
						  
						  cptr->new_delta = 128;
						  cptr->ddelta = cptr->p_olddelta;
                          break;
                case 0x3: if(cptr->period!=cptr->new_period){
                          if((effect&0xFF)==0){
                               if(cptr->period>cptr->new_period){
                                      cptr->p_delta=-cptr->p_olddelta;
									  cptr->p_down=(unsigned short)cptr->new_period;
									  cptr->p_up=1900;
                                }else{cptr->p_delta=cptr->p_olddelta;cptr->p_up=(unsigned short)cptr->new_period;cptr->p_down=10;}
                          }else{if(cptr->period>cptr->new_period){
                                      cptr->p_delta=-(effect&0xFF);cptr->p_down=(unsigned short)cptr->new_period;cptr->p_up=1900;
                                }else{cptr->p_delta=(effect&0xFF);cptr->p_up=(unsigned short)cptr->new_period;cptr->p_down=10;}
                                cptr->p_olddelta=(effect&0xFF);
                               }
                          }
						  cptr->new_delta = U->_sampleticksconst/cptr->new_period;
						  cptr->ddelta = cptr->p_olddelta;
                          break;
				case 0xA: cptr->v_up = (effect&0xF0)>>4;
					      cptr->v_down = effect&0x0F;
						  break;
				case 0xE: switch( (effect>>4)&15 ) {
								case 0x1: cptr->period -= effect&15;
									      cptr->delta = U->_sampleticksconst/cptr->period;
										  break;
								case 0x2: cptr->period += effect&15;
									      cptr->delta = U->_sampleticksconst/cptr->period;
										  break;
								case 0xA: cptr->volume += effect&15; 
								          if( cptr->volume > 64 ) cptr->volume = 64;
										  break;
								case 0xB: cptr->volume -= effect&15;
								          if( cptr->volume < 0 ) cptr->volume = 0;
										  break;
						  }
						  break;
                case 0xF: if((ulong)(effect&0xFF)<=31){
                            U->_speed=(ulong)(effect&0xFF);U->_sp=U->_speed;
                          }else{
                            U->_bpm=(ulong)(effect&0xFF);U->_onetick=(((ulong)U->_playrate*25)<<8)/(U->_bpm*10);
                          }
                          break;
                case 0xD: U->_patternpos = (effect & 0xFF) * 4; U->_tablepos++; if(U->_tablepos>=_song->length){U->_tablepos=0;} break;
                case 0xC: cptr->volume = (effect & 0xFF); break;
                case 0xB: U->_tablepos = (effect & 0xFF); U->_patternpos = 0; break;
                case 0x9: cptr->ticks = ((effect & 0xFF) * 256) << 16; break;
				case 0x6: 
					cptr->wah_amp = (effect & 0xFF) * 64;
					break;
				case 0x7: 
					cptr->cutoff = effect & 0xFF; 
					cptr->res_delta = cptr->cutoff<<17;
					break;
				case 0x8: 
					if( (effect&0xff) == 0xFF ) 
					{
						cptr->pitch_ticks = 0; //RESET PITCH
						break;
					}
					if( cptr->pitch == 0 ) cptr->pitch_ticks = cptr->ticks;
					if( cptr->period )
					cptr->pitch = U->_sampleticksconst / 
						_fine[((_song->samples[cptr->sampnum]->finetune*60)+(effect&0xff)+12)];
					break;
        }
}

void effect(channel * cptr, user_info *U)
{
  if(cptr->v_up!=0) {
    cptr->volume += cptr->v_up;
	if(cptr->volume > 63) {cptr->volume = 63; cptr->v_up = 0;}
  }
  if(cptr->v_down!=0) {
    cptr->volume -= cptr->v_down;
	if(cptr->volume < 0) {cptr->volume = 0; cptr->v_down = 0;}
  }
  if( cptr->p_delta!=0 && (cptr->spec_effects&SF_SPORTAMENTO)==0 ){
    cptr->period+=cptr->p_delta;
    if(cptr->period>=cptr->p_up){cptr->period=cptr->p_up;cptr->p_delta=0;}
    if(cptr->period<=cptr->p_down){cptr->period=cptr->p_down;cptr->p_delta=0;}
  if(cptr->period!=0){
    cptr->delta = U->_sampleticksconst/cptr->period;
  }}
  
  //Pitch:
  if( cptr->pitch )
  {
	cptr->ticks = cptr->pitch_ticks;
	START_ANTICLICK;
  }
}


void convert_all( user_info *U )
{
//  U->_buffer=ByteSwap32(U->_buffer);
  U->_channels=(channel*)ByteSwap32(U->_channels);
  U->_patternticks=ByteSwap32(U->_patternticks);
  U->_onetick=ByteSwap32(U->_onetick);
  U->_sp=ByteSwap32(U->_sp);
  U->_speed=ByteSwap32(U->_speed);
  U->_bpm=ByteSwap32(U->_bpm);
//  U->_patterndata=ByteSwap32(U->_patterndata);
//  U->_song=ByteSwap32(U->_song);
  U->_one=(channel*)ByteSwap32(U->_one);
  U->_fine=(unsigned short*)ByteSwap32(U->_fine);
  U->_sampleticksconst=ByteSwap32(U->_sampleticksconst);
  U->_pp=(char*)ByteSwap32(U->_pp);
//  U->_sampledata=ByteSwap32(U->_sampledata);
  U->_update=ByteSwap32(U->_update);

  U->_tablepos=(unsigned short)ByteSwap16(U->_tablepos);
  U->_patternpos=(unsigned short)ByteSwap16(U->_patternpos);
  U->_num_of_mods=(unsigned short)ByteSwap16(U->_num_of_mods);
  U->_playrate=(unsigned short)ByteSwap16(U->_playrate);
}

void convert_channel(channel *cc)
{
  cc->echo_volume=ByteSwap32(cc->echo_volume);
  cc->sampdata=(char*)ByteSwap32(cc->sampdata);
  cc->length=(unsigned short)ByteSwap16(cc->length);
  cc->replen=(unsigned short)ByteSwap16(cc->replen);
  cc->reppnt=(unsigned short)ByteSwap16(cc->reppnt);
  cc->delta=ByteSwap32(cc->delta);
  cc->ticks=ByteSwap32(cc->ticks);
  cc->rep=(unsigned short)ByteSwap16(cc->rep);
}