shithub: itdec

ref: 3e8dbce97cd4b03b43f226aaf406b8809dba998b
dir: /itdec.c/

View raw version
#include "it_struc.h"

#define MIN(a,b) ((a)<(b)?(a):(b))

static int tempo = 125;
static double pos;
static s32int mixbuf[44100*2];
static s16int outbuf[44100*2];

static const char *
DriverDetectCard(it_engine *, const char *, uint16_t, uint16_t)
{
	return nil;
}

static const char *
DriverInitSound(it_engine *)
{
	return nil;
}

int
DriverUninitSound(it_engine *)
{
	return 0;
}

static void
kill_channel(it_engine *ite, it_slave *slave)
{
	slave->Flags = 0x0200;
	slave->LpD = 0;

	if((slave->HCN & 0x80) == 0)
		ite->chn[slave->HCN].Flags &= ~4;
}

static int
update_offs(it_engine *ite, it_slave *slave, int32_t *offs, int32_t *oferr, int32_t *nfreq, const int32_t lpbeg, const int32_t lpend)
{
	*oferr += *nfreq;
	*offs += *oferr>>16;
	*oferr &= 0xFFFF;

	if((slave->LpM & 24) == 24 && slave->LpD != 0 && (*offs < lpbeg)){
		*offs = lpbeg - *offs;
		if(lpend <= lpbeg){
			kill_channel(ite, slave);
			return 1;
		}

		*offs %= (lpend-lpbeg)*2;
		if(*offs < (lpend-lpbeg)){
			slave->LpD = 0;
			if(*nfreq < 0) *nfreq = -*nfreq;
			*offs += lpbeg;
		}else{
			*offs = (lpend - lpbeg) - *offs;
			*offs += lpend - 1;
		}
	}

	if(*offs < 0){
		if((slave->LpM & 24) != 24){
			kill_channel(ite, slave);
			slave->LpD = 0;
			return 1;
		}

		*offs = 0;
		if(*nfreq < 0) *nfreq = -*nfreq;
		slave->LpD = 0;
	}

	if(*offs >= lpend){
		if((slave->LpM & 8) == 0){
			kill_channel(ite, slave);
			return 1;
		}

		if((slave->LpM & 24) == 24){
			if(lpend <= lpbeg){
				kill_channel(ite, slave);
				return 1;
			}

			*offs -= lpend;
			*offs %= (lpend-lpbeg)*2;
			if(*offs >= (lpend-lpbeg)){
				*offs -= (lpend - lpbeg);
				*offs += lpbeg;
			}else{
				slave->LpD = 1;

				*offs = lpend - *offs - 1;

				if(*nfreq > 0)
					*nfreq = -*nfreq;
			}
		}else{
			if(lpend <= lpbeg){
				kill_channel(ite, slave);
				return 1;
			}

			*offs -= lpend;
			*offs %= (lpend-lpbeg);
			*offs += lpbeg;
		}
	}

	if(*offs < 0 || *offs >= lpend){
		kill_channel(ite, slave);
		return 1;
	}

	return 0;
}

static int
DriverPoll(it_engine *ite, uint16_t PlayMode, uint16_t CurrentPattern)
{
	it_slave *slave;
	u16int ax, cx;
	it_slave *si;
	int i, j;

	USED(PlayMode, CurrentPattern);
	Update(ite, &cx, &si, &ax);

	int tper = (44100*10)/(tempo*4);

	memset(mixbuf, 0, tper*2*4);
	memset(outbuf, 0, tper*2*2);

	for(i = 0; i < ite->NumChannels; i++){
		slave = &ite->slave[i];
		if((slave->Flags & 1) != 0 && (slave->Flags & 0x0200) == 0){
			int offs = slave->Sample_Offset;
			int oferr = slave->SmpErr;
			int lpbeg = slave->Loop_Beginning;
			int lpend = slave->Loop_End;
			int nfreq = slave->Frequency;

			int32_t vol = slave->_16bVol;
			vol *= ite->hdr.MV;
			vol >>= 8;

			int lvol = vol, rvol = vol;

			if(slave->FPP == 100)
				rvol = -rvol;
			else if(slave->FPP < 32)
				rvol = (rvol * slave->FPP) >> 5;
			else
				lvol = (lvol * (64 - slave->FPP)) >> 5;

			nfreq = (((s64int)nfreq) << 16) / 44100ULL;

			if(slave->LpD == 1) nfreq = -nfreq;

			if(ite->SamplePointer[slave->Smp] == nil){
				kill_channel(ite, slave);
				break;
			}else if(slave->Bit != 0){
				s16int *data = (s16int*)ite->SamplePointer[slave->Smp];

				for(j = 0; j < tper*2; j+=2){
					if(update_offs(ite, slave, &offs, &oferr, &nfreq, lpbeg, lpend) != 0)
						break;

					mixbuf[j+0] -= (lvol*(int32_t)data[offs])>>14;
					mixbuf[j+1] -= (rvol*(int32_t)data[offs])>>14;
				}
			}else{
				s8int *data = (s8int*)ite->SamplePointer[slave->Smp];
				for(j = 0; j < tper*2; j+=2){
					if(update_offs(ite, slave, &offs, &oferr, &nfreq, lpbeg, lpend) != 0)
						break;

					mixbuf[j+0] -= (lvol*(int32_t)data[offs])>>(14-8);
					mixbuf[j+1] -= (rvol*(int32_t)data[offs])>>(14-8);
				}
			}
			slave->Sample_Offset = offs;
			slave->SmpErr = oferr;
		}

		slave->Flags &= 0x788D;
	}

	for(i = 0; i < tper*2; i++){
		int32_t v = mixbuf[i];
		if(v >  0x7FFF)
			v =  0x7FFF;
		else if(v < -0x7FFF)
			v = -0x7FFF;
		outbuf[i] = v;
	}

	int got = tper*2*2;
	int skip = 0;
	if(pos > 0.001){
		double n = MIN(pos, (double)got / 44100.0);
		int skip = (int)(n * 44100)*2*2;
		pos -= n;
		if(skip >= got)
			return 0;
	}
	write(1, outbuf+skip, got-skip);

	return 0;
}

static int
DriverSetTempo(it_engine *, uint16_t Tempo)
{
	tempo = Tempo;
	return 0;
}

static int
DriverSetMixVolume(it_engine *ite, uint16_t MixVolume)
{
	USED(ite, MixVolume);
	return 0;
}

static int
DriverSetStereo(it_engine *ite, uint16_t Stereo)
{
	/* whoops */
	USED(ite, Stereo);
	return 0;
}

static int
DriverReleaseSample(it_engine *, it_sample *)
{
	return 0;
}

static int
DriverMIDIOut(it_engine *, uint8_t)
{
	return 0;
}

static int
DriverGetWaveform(it_engine *)
{
	return 0;
}

it_drvdata *
drv_oss_init(it_engine *)
{
	static it_drvdata d = {
		.BasePort = 0xFFFF,
		.IRQ = 0xFFFF,
		.DMA = 0xFFFF,
		.CmdLineMixSpeed = 0,
		.CmdLineDMASize = 1024,
		.ReverseChannels = 0,
		.DriverMaxChannels = 256,
		.StopEndOfPlaySection = 1,
		.DefaultChannels = 256,
		.DriverFlags = 0,
		.DriverDetectCard = DriverDetectCard,
		.DriverInitSound = DriverInitSound,
		.DriverUninitSound = DriverUninitSound,
		.DriverPoll = DriverPoll,
		.DriverSetTempo = DriverSetTempo,
		.DriverSetMixVolume = DriverSetMixVolume,
		.DriverSetStereo = DriverSetStereo,
		.DriverReleaseSample = DriverReleaseSample,
		.DriverMIDIOut = DriverMIDIOut,
		.DriverGetWaveform = DriverGetWaveform,
	};

	return &d;
}

it_drvdata *
drv_sdl_init(it_engine *)
{
	return nil;
}

static void
usage(void)
{
	fprint(2, "usage: %s [-s SECONDS]\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	it_engine *ite;
	char *s;

	ARGBEGIN{
	case 's':
		pos = atof(EARGF(usage()));
		assert(pos >= 0.0);
		break;
	default:
		usage();
	}ARGEND

	ite = ITEngineNew();
	Music_InitMusic(ite);
	Music_AutoDetectSoundCard(ite);
	D_LoadIT(ite, 0);
	Music_PlaySong(ite, 0);
	s = (char*)ite->hdr.SongName;
	if(*s != 0)
		fprint(2, "%.*s\n", sizeof(ite->hdr.SongName), s);
	if(pos > 0.0)
		fprint(2, "time: %g\n", pos);
	while(ite->PlayMode != 0)
		Music_Poll(ite);
}