/* These #defines are TEMPORARY. They are used to write alternative code to * handle ambiguities in the format specification. The correct code in each * case will be determined most likely by experimentation. */ #define STEREO_SAMPLES_COUNT_AS_TWO #define INVALID_ORDERS_END_SONG #define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP #define VOLUME_OUT_OF_RANGE_SETS_MAXIMUM #include //temporary /* _______ ____ __ ___ ___ * \ _ \ \ / \ / \ \ / / ' ' ' * | | \ \ | | || | \/ | . . * | | | | | | || ||\ /| | * | | | | | | || || \/ | | ' ' ' * | | | | | | || || | | . . * | |_/ / \ \__// || | | * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque * / \ * / . \ * cit.c - IT Compiler / / \ \ * | < / \_ * Derived from an IT file loader by Bob. | \/ /\ / * \_ / > / * Written by entheh. Barely works. A complete | \ / / * mess. | ' / * \__/ */ #include #include #include "allegro.h" #define MUSIC_IT AL_ID('I','M','P','M') typedef struct MODULE_MUSIC_INFO { char Name[29]; int Type; } MODULE_MUSIC_INFO; #define ENVELOPE_ON 1 #define ENVELOPE_LOOP_ON 2 #define ENVELOPE_SUSTAINLOOP 4 #define ENVELOPE_PITCH_IS_FILTER 128 typedef struct MODULE_ENVELOPE { unsigned char Flag, NumNodes, LoopBegin, LoopEnd, SustainLoopBegin, SustainLoopEnd; //in nodes. char NodeY[25]; short NodeTick[25]; } MODULE_ENVELOPE; typedef struct MODULE_VENVELOPE { unsigned char NextNode; unsigned short CurTick; } MODULE_VENVELOPE; #define NNA_NOTECUT 0 #define NNA_NOTECONTINUE 1 #define NNA_NOTEOFF 2 #define NNA_NOTEFADE 3 #define DCT_OFF 0 #define DCT_NOTE 1 #define DCT_SAMPLE 2 #define DCT_INSTRUMENT 3 #define DCA_CUT 0 #define DCA_NOTEOFF 1 #define DCA_NOTEFADE 2 typedef struct MODULE_INSTRUMENT { //unsigned char Flag; char VolumeLoopNodeStart, VolumeLoopNodeEnd; char SustainLoopNodeStart, SustainLoopNodeEnd; char DuplicateCheckType; char DuplicateCheckAction; char NewNoteAction; int FadeOut; unsigned char PitchPanSeparation, //0->64, Bit7: Don't use PitchPanCenter; //Note, from C-0 to B-9 unsigned char GlobalVolume, //0->128 DefaultPan; //0->64, Bit7: Don't use unsigned char RandomVolume, RandomPanning; unsigned char FilterCutOff; unsigned char FilterResonance; unsigned char NoteSample[120]; unsigned char NoteNote[120]; MODULE_ENVELOPE VolumeEnvelope, PanningEnvelope, PitchEnvelope; } MODULE_INSTRUMENT; #define SAMPLE_HASSAMPLE 1 #define SAMPLE_16BIT 2 #define SAMPLE_STEREO 4 #define SAMPLE_USELOOP 16 #define SAMPLE_USESUSTAINLOOP 32 #define SAMPLE_PINGPONGLOOP 64 #define SAMPLE_PINGPONGSUSTAINLOOP 128 #define VIBRATO_SINE 0 #define VIBRATO_RAMPDOWN 1 #define VIBRATO_SQUARE 2 #define VIBRATO_RANDOM 3 typedef struct MODULE_SAMPLE { unsigned char GlobalVolume; //0->64 unsigned char Flag; unsigned char DefaultVolume; unsigned char DefaultPanning; int SampleLength; //in samples, not bytes ! int LoopBegin, LoopEnd; //in samples int SustainLoopBegin, SustainLoopEnd; int C5Speed; //Number of bytes/sec for C-5 SAMPLE *Sample; char VibratoSpeed; //0->64 char VibratoDepth; //0->64 char VibratoWaveForm; char VibratoRate; //0->64 } MODULE_SAMPLE; #define NOTEMASK_NOTE 1 #define NOTEMASK_INSTRUMENT 2 #define NOTEMASK_VOLPAN 4 #define NOTEMASK_COMMAND 8 #define NOTE_OFF 255 #define NOTE_CUT 254 typedef struct MODULE_NOTE { char Mask; char Channel; //if -1, then end of row. unsigned char Note; char Instrument; unsigned char VolPan; unsigned char Command, CommandValue; } MODULE_NOTE; typedef struct MODULE_PATTERN { int NumRows; int NumNotes; MODULE_NOTE *Note; } MODULE_PATTERN; #define VCHANNEL_PLAYING 1 #define VCHANNEL_BACKGROUND 2 #define VCHANNEL_FADING 4 #define VCHANNEL_RETRIG 8 struct MODULE_CHANNEL; typedef struct MODULE_VCHANNEL { unsigned char Flag; unsigned char Instrument; unsigned char Note; unsigned int Volume; unsigned char Pan; signed char PanSep; MODULE_SAMPLE *sample; int pitch; /* 0 corresponds to C-5, 256 is one semitone. */ MODULE_VENVELOPE VVolEnv; int fadeoutcount; /* WARNING: search for BLEARGH when adding to this struct. */ /* MODULE_SAMPLE *Sample; //NULL is unused char voice; char ChannelVolume; char NoteOn; char NNA; short FadeOutCount, FadeOut; float MixVolume, MixPan; MODULE_VENVELOPE *VVolumeEnvelope, *VPanningEnvelope, *VPitchEnvelope; struct MODULE_VCHANNEL *next, *prev; */ unsigned char NNA; unsigned int DUHvol, DUHpan; struct MODULE_CHANNEL *channel; } MODULE_VCHANNEL; #define CHANNEL_RETRIG 1 typedef struct MODULE_CHANNEL { unsigned int ID; unsigned char Flag; unsigned char Note; unsigned char Instrument; unsigned char Volume; //0->64 unsigned char Pan; //0->32->64, 100 = surround, Bit7: Disable signed char PanSep; int pitch; unsigned char ChannelVolume; unsigned char lastvolslide; unsigned char lastDKL, lastN, lastW, lastEF, lastG, lastT; int portamento, toneporta; signed char volslide; signed char channelvolslide; MODULE_SAMPLE *sample; MODULE_VCHANNEL *VChannel; } MODULE_CHANNEL; #define FLAG_STEREO 1 #define FLAG_USEINSTRUMENTS 4 #define FLAG_LINEARSLIDES 8 /* If not set, use Amiga slides */ #define FLAG_OLDEFFECT 16 #define FLAG_COMPATIBLEGXX 32 #define ORDER_END 255 #define ORDER_SKIP 254 typedef struct MODULE { MODULE_INSTRUMENT *Instrument; MODULE_SAMPLE *Sample; MODULE_PATTERN *Pattern; int NumOrders; int NumInstruments; int NumSamples; int NumPatterns; int Flags; short Version; unsigned char GlobalVolume; signed char globalvolslide; unsigned char MixVolume; unsigned char Speed, Tempo, tick; signed char temposlide; unsigned char PanningSeparation; unsigned char *Order; MODULE_CHANNEL Channel[64]; MODULE_VCHANNEL VChannel[256]; int processorder; int processrow; int breakrow; int rowcount; } MODULE; #define IT_SET_SPEED 1 #define IT_JUMP_TO_ORDER 2 #define IT_BREAK_TO_ROW 3 #define IT_VOLUME_SLIDE 4 #define IT_PORTAMENTO_DOWN 5 #define IT_PORTAMENTO_UP 6 #define IT_TONE_PORTAMENTO 7 #define IT_VIBRATO 8 #define IT_TREMOR 9 #define IT_ARPEGGIO 10 #define IT_VOLSLIDE_VIBRATO 11 #define IT_VOLSLIDE_TONEPORTA 12 #define IT_SET_CHANNEL_VOLUME 13 #define IT_CHANNEL_VOLUME_SLIDE 14 #define IT_SET_SAMPLE_OFFSET 15 #define IT_PANNING_SLIDE 16 #define IT_RETRIGGER_NOTE 17 #define IT_TREMOLO 18 #define IT_S 19 #define IT_SET_SONG_TEMPO 20 #define IT_FINE_VIBRATO 21 #define IT_SET_GLOBAL_VOLUME 22 #define IT_GLOBAL_VOLUME_SLIDE 23 #define IT_SET_PANNING 24 #define IT_PANBRELLO 25 #define IT_MIDI_MACRO 26 //see MIDI.TXT /* These represent the top nibble of the command value. */ #define IT_S_SET_FILTER 0 /* Greyed out in IT... */ #define IT_S_SET_GLISSANDO_CONTROL 1 /* Greyed out in IT... */ #define IT_S_FINETUNE 2 /* Greyed out in IT... */ #define IT_S_SET_VIBRATO_WAVEFORM 3 #define IT_S_SET_TREMOLO_WAVEFORM 4 #define IT_S_SET_PANBRELLO_WAVEFORM 5 #define IT_S_FINE_PATTERN_DELAY 6 #define IT_S7 7 #define IT_S_SET_PAN 8 #define IT_S_SET_SURROUND_SOUND 9 #define IT_S_SET_HIGH_OFFSET 10 #define IT_S_PATTERN_LOOP 11 #define IT_S_DELAYED_NOTE_CUT 12 #define IT_S_NOTE_DELAY 13 #define IT_S_PATTERN_DELAY 14 #define IT_S_SET_MIDI_MACRO 15 /* S0x Set filter S1x Set glissando control S2x Set finetune S3x Set vibrato waveform to type x S4x Set tremelo waveform to type x S5x Set panbrello waveform to type x Waveforms for commands S3x, S4x and S5x: 0: Sine wave 1: Ramp down 2: Square wave 3: Random wave S6x Pattern delay for x ticks S70 Past note cut S71 Past note off S72 Past note fade S73 Set NNA to note cut S74 Set NNA to continue S75 Set NNA to note off S76 Set NNA to note fade S77 Turn off volume envelope S78 Turn on volume envelope S79 Turn off panning envelope S7A Turn on panning envelope S7B Turn off pitch envelope S7C Turn on pitch envelope S8x Set panning position S91 Set surround sound SAy Set high value of sample offset yxx00h SB0 Set loopback point SBx Loop x times to loopback point SCx Note cut after x ticks SDx Note delay for x ticks SEx Pattern delay for x rows SFx Set parameterised MIDI Macro */ typedef struct MODULE_PLAY { MODULE *Music; int Loop, Tick; int CurOrder, CurPattern, CurPos; int Command, CommandVal0, CommandVal1, CommandVal2; int pos; } MODULE_PLAY; extern MODULE_PLAY *song; extern int IT_Play_Method; MODULE *load_it(char*); //int get_module_size(MODULE *); int play_it(MODULE *j, int loop); void install_module(); void set_mix_volume(int i); void stop_it(); int is_music_done(); void destroy_it(MODULE *j); //Should be internal: extern MODULE_PLAY *song; extern int note_freq[120]; //extern void MOD_Interrupt(...); extern int MOD_Poller(void*); #define IT_TIMER 0 #define IT_POLL 1 /* typedef.hpp */ typedef unsigned char byte; typedef unsigned short word; typedef unsigned int dword; /* load_it.cpp */ /* int detect_it(char *f) { int sig; PACKFILE *fn = pack_fopen(f, F_READ); if (fn == NULL) return FALSE; sig = pack_mgetl(fn); if (sig != MUSIC_IT) { pack_fclose(fn); return FALSE; } pack_fclose(fn); return TRUE; } */ MODULE *create_it() { MODULE *m = (MODULE*)malloc(sizeof(MODULE)); if (!m) return NULL; memset(m, 0, sizeof(MODULE)); return m; } void destroy_it(MODULE *j) { int i; if (j) { /* Remove patterns. */ if (j->Pattern) { for (i = 0; i < j->NumPatterns; i++) free(j->Pattern[i].Note); free(j->Pattern); } /* Remove instruments. */ free(j->Instrument); /* Remove samples. */ if (j->Sample) { for (i = 0; i < j->NumSamples; i++) destroy_sample(j->Sample[i].Sample); free(j->Sample); } /* Remove orders. */ free(j->Order); free(j); } } //#define DEBUG_IT_SIZE /* int get_module_size(MODULE *j) { int a, b, c, d = 0, e; int i; a = sizeof(MODULE) + j->NumOrders; b = j->NumInstruments * sizeof(MODULE_INSTRUMENT); c = j->NumSamples * sizeof(MODULE_SAMPLE); for (i=0; iNumSamples; i++) d += j->Sample[i].SampleLength * (j->Sample[i].Flag & 2 ? sizeof(short) : 1) * (j->Sample[i].Flag & 4 ? 2: 1); e = 4 + sizeof(MODULE_PATTERN) * j->NumPatterns; for (i=0; iNumPatterns; i++) e += j->Pattern[i].NumNotes * sizeof(MODULE_NOTE); #ifdef DEBUG_IT_SIZE fprintf(stderr, "Base: %i, Instruments(%i): %i, Samples(%i): %i, Data: %i, Patterns(%i): %i\n", a, j->NumInstruments, b, j->NumSamples, c, d, j->NumPatterns, e); #endif return a+b+c+d+e; } */ #define MAX_IT_CHN 64 //#define DEBUG_HEADER //#define DEBUG_INSTRUMENTS //#define DEBUG_SAMPLES //#define DEBUG_PATTERNS static dword *sourcebuf = NULL; static dword *sourcepos = NULL; static byte rembits = 0; int readblock(PACKFILE *f) { long size; int c = pack_igetw(f); if (c == -1) return 0; size = c; sourcebuf = (dword*)malloc(size+4); if (!sourcebuf) return 0; c = pack_fread(sourcebuf, size, f); if (c < 1) { free(sourcebuf); sourcebuf = NULL; return 0; } sourcepos = sourcebuf; rembits = 32; return 1; } void freeblock() { if (sourcebuf) free(sourcebuf); sourcebuf = NULL; } dword readbits(char b) { dword val; if (b <= rembits) { val = *sourcepos & ((1 << b) - 1); *sourcepos >>= b; rembits -= b; } else { dword nbits = b - rembits; val = *sourcepos; sourcepos++; val |= ((*sourcepos & ((1 << nbits) - 1)) << rembits); *sourcepos >>= nbits; rembits = 32 - nbits; } return val; } void decompress8(PACKFILE *f, void *data, int len, int tver) { char *destbuf = (char*)data; char *destpos = destbuf; int blocklen, blockpos; byte bitwidth; word val; char d1, d2; memset(destbuf, 0, len); while (len>0) { //Read a block of compressed data: if (!readblock(f)) return; //Set up a few variables blocklen = (len < 0x8000) ? len : 0x8000; //Max block length is 0x8000 bytes blockpos = 0; bitwidth = 9; d1 = d2 = 0; //Start the decompression: while (blockpos < blocklen) { //Read a value: val = readbits(bitwidth); //Check for bit width change: if (bitwidth < 7) { //Method 1: if (val == (1 << (bitwidth - 1))) { val = readbits(3) + 1; bitwidth = (val < bitwidth) ? val : val + 1; continue; } } else if (bitwidth < 9) { //Method 2 byte border = (0xFF >> (9 - bitwidth)) - 4; if (val > border && val <= (border + 8)) { val -= border; bitwidth = (val < bitwidth) ? val : val + 1; continue; } } else if (bitwidth == 9) { //Method 3 if (val & 0x100) { bitwidth = (val + 1) & 0xFF; continue; } } else { //Illegal width, abort ? freeblock(); return; } //Expand the value to signed byte: { char v; //The sample value: if (bitwidth < 8) { byte shift = 8 - bitwidth; v = (val << shift); v >>= shift; } else v = (char)val; //And integrate the sample value //(It always has to end with integration doesn't it ? ;-) d1 += v; d2 += d1; } //Store ! *destpos = ((tver == 0x215) ? d2 : d1); destpos++; blockpos++; } freeblock(); len -= blocklen; } return; } void decompress16(PACKFILE *f, void *data, int len, int tver) { //make the output buffer: short *destbuf = (short*)data; short *destpos = destbuf; int blocklen, blockpos; byte bitwidth; long val; short d1, d2; memset(destbuf, 0, len); while (len>0) { //Read a block of compressed data: if (!readblock(f)) return; //Set up a few variables blocklen = (len < 0x4000) ? len : 0x4000; // Max block length is 0x4000 bytes blockpos = 0; bitwidth = 17; d1 = d2 = 0; //Start the decompression: while (blockpos < blocklen) { val = readbits(bitwidth); //Check for bit width change: if (bitwidth < 7) { //Method 1: if (val == (1 << (bitwidth - 1))) { val = readbits(4) + 1; bitwidth = (val < bitwidth) ? val : val + 1; continue; } } else if (bitwidth < 17) { //Method 2 word border = (0xFFFF >> (17 - bitwidth)) - 8; if (val > border && val <= (border + 16)) { val -= border; bitwidth = val < bitwidth ? val : val + 1; continue; } } else if (bitwidth == 17) { //Method 3 if (val & 0x10000) { bitwidth = (val + 1) & 0xFF; continue; } } else { //Illegal width, abort ? freeblock(); return; } //Expand the value to signed byte: { short v; //The sample value: if (bitwidth < 16) { byte shift = 16 - bitwidth; v = (val << shift); v >>= shift; } else v = (short)val; //And integrate the sample value //(It always has to end with integration doesn't it ? ;-) d1 += v; d2 += d1; } //Store ! *destpos = ((tver == 0x215) ? d2 : d1); destpos++; blockpos++; } freeblock(); len -= blocklen; } return; } void read_envelopes(PACKFILE *f, MODULE *j, int i) { int k; j->Instrument[i].VolumeEnvelope.Flag = pack_getc(f); j->Instrument[i].VolumeEnvelope.NumNodes = pack_getc(f); j->Instrument[i].VolumeEnvelope.LoopBegin = pack_getc(f); j->Instrument[i].VolumeEnvelope.LoopEnd = pack_getc(f); j->Instrument[i].VolumeEnvelope.SustainLoopBegin = pack_getc(f); j->Instrument[i].VolumeEnvelope.SustainLoopEnd = pack_getc(f); for (k = 0; k < j->Instrument[i].VolumeEnvelope.NumNodes; k++) { j->Instrument[i].VolumeEnvelope.NodeY[k] = pack_getc(f); j->Instrument[i].VolumeEnvelope.NodeTick[k] = pack_igetw(f); } pack_fseek(f, 75 - j->Instrument[i].VolumeEnvelope.NumNodes * 3); j->Instrument[i].PanningEnvelope.Flag = pack_getc(f); j->Instrument[i].PanningEnvelope.NumNodes = pack_getc(f); j->Instrument[i].PanningEnvelope.LoopBegin = pack_getc(f); j->Instrument[i].PanningEnvelope.LoopEnd = pack_getc(f); j->Instrument[i].PanningEnvelope.SustainLoopBegin = pack_getc(f); j->Instrument[i].PanningEnvelope.SustainLoopEnd = pack_getc(f); for (k = 0; k < j->Instrument[i].PanningEnvelope.NumNodes; k++) { j->Instrument[i].PanningEnvelope.NodeY[k] = pack_getc(f); j->Instrument[i].PanningEnvelope.NodeTick[k] = pack_igetw(f); } pack_fseek(f, 75 - j->Instrument[i].PanningEnvelope.NumNodes * 3); j->Instrument[i].PitchEnvelope.Flag = pack_getc(f); j->Instrument[i].PitchEnvelope.NumNodes = pack_getc(f); j->Instrument[i].PitchEnvelope.LoopBegin = pack_getc(f); j->Instrument[i].PitchEnvelope.LoopEnd = pack_getc(f); j->Instrument[i].PitchEnvelope.SustainLoopBegin = pack_getc(f); j->Instrument[i].PitchEnvelope.SustainLoopEnd = pack_getc(f); for (k = 0; k < j->Instrument[i].PitchEnvelope.NumNodes; k++) { j->Instrument[i].PitchEnvelope.NodeY[k] = pack_getc(f); j->Instrument[i].PitchEnvelope.NodeTick[k] = pack_igetw(f); } } MODULE *load_old_instrument(char *file, long offset, MODULE *j, int i) { int k; PACKFILE *f = pack_fopen(file, F_READ); if (!f) { #ifdef DEBUG_INSTRUMENTS fprintf(stderr, "Error reopening!\n"); #endif destroy_it(j); return NULL; } pack_fseek(f, offset); if (pack_mgetl(f) != AL_ID('I','M','P','I')) { destroy_it(j); return NULL; } /* Skip DOS Filename */ pack_fseek(f, 13); j->Instrument[i].VolumeEnvelope.Flag = pack_getc(f); j->Instrument[i].VolumeEnvelope.LoopBegin = pack_getc(f); j->Instrument[i].VolumeEnvelope.LoopEnd = pack_getc(f); j->Instrument[i].VolumeEnvelope.SustainLoopBegin = pack_getc(f); j->Instrument[i].VolumeEnvelope.SustainLoopEnd = pack_getc(f); /* Two unused bytes */ pack_fseek(f, 2); /* OLD FadeOut: Ranges between 0 and 64, but the fadeout "Count" is 512. * NEW FadeOut: Ranges between 0 and 128, but the fadeout "Count" is 1024 * * TODO: Find out what this means, and make adjustments accordingly. */ j->Instrument[i].FadeOut = pack_igetw(f) << 1; j->Instrument[i].NewNoteAction = pack_getc(f); j->Instrument[i].DuplicateCheckType = pack_getc(f); j->Instrument[i].DuplicateCheckAction = DCA_CUT; /* This might be wrong! */ /* Skip Tracker Version and Number of Samples. These are only used in * separate instrument files. Also skip unused bytes and Instrument Name. */ pack_fseek(f, 36); j->Instrument[i].PitchPanSeparation = 0; j->Instrument[i].PitchPanCenter = 60; j->Instrument[i].GlobalVolume = 128; /* Should this be 64 or something? */ j->Instrument[i].DefaultPan = 32; /* Should this be 128? */ j->Instrument[i].RandomVolume = 0; j->Instrument[i].RandomPanning = 0; for (k = 0; k < 120; k++) { j->Instrument[i].NoteNote[k] = pack_getc(f); j->Instrument[i].NoteSample[k] = pack_getc(f); } /* Skip "Volume envelope (200 bytes)" - need to know better what this is * for though. */ pack_fseek(f, 200); fprintf(stderr, "Inst %02d Env:", i); for (j->Instrument[i].VolumeEnvelope.NumNodes = 0; j->Instrument[i].VolumeEnvelope.NumNodes < 25; j->Instrument[i].VolumeEnvelope.NumNodes++) { j->Instrument[i].VolumeEnvelope.NodeTick[k] = pack_getc(f); j->Instrument[i].VolumeEnvelope.NodeY[k] = pack_getc(f); fprintf(stderr, " %d,%d", j->Instrument[i].VolumeEnvelope.NodeTick[k], j->Instrument[i].VolumeEnvelope.NodeY[k]); } pack_fseek(f, 50 - j->Instrument[i].VolumeEnvelope.NumNodes * 2); fprintf(stderr, "\n"); j->Instrument[i].FilterCutOff = 0; //Are these the right values j->Instrument[i].FilterResonance = 0; //to disable the filter? j->Instrument[i].PanningEnvelope.Flag = 0; j->Instrument[i].PanningEnvelope.NumNodes = 0; j->Instrument[i].PanningEnvelope.LoopBegin = 0; j->Instrument[i].PanningEnvelope.LoopEnd = 0; j->Instrument[i].PanningEnvelope.SustainLoopBegin = 0; j->Instrument[i].PanningEnvelope.SustainLoopEnd = 0; j->Instrument[i].PitchEnvelope.Flag = 0; j->Instrument[i].PitchEnvelope.NumNodes = 0; j->Instrument[i].PitchEnvelope.LoopBegin = 0; j->Instrument[i].PitchEnvelope.LoopEnd = 0; j->Instrument[i].PitchEnvelope.SustainLoopBegin = 0; j->Instrument[i].PitchEnvelope.SustainLoopEnd = 0; pack_fclose(f); if (errno) { destroy_it(j); return NULL; } return j; } MODULE *load_instrument(char *file, long offset, MODULE *j, int i) { int k; PACKFILE *f = pack_fopen(file, F_READ); if (!f) { #ifdef DEBUG_INSTRUMENTS fprintf(stderr, "Error reopening!\n"); #endif destroy_it(j); return NULL; } pack_fseek(f, offset); if (pack_mgetl(f) != AL_ID('I','M','P','I')) { destroy_it(j); return NULL; } /* Skip DOS Filename */ pack_fseek(f, 13); j->Instrument[i].NewNoteAction = pack_getc(f); j->Instrument[i].DuplicateCheckType = pack_getc(f); j->Instrument[i].DuplicateCheckAction = pack_getc(f); j->Instrument[i].FadeOut = pack_igetw(f); j->Instrument[i].PitchPanSeparation = pack_getc(f); j->Instrument[i].PitchPanCenter = pack_getc(f); j->Instrument[i].GlobalVolume = pack_getc(f); j->Instrument[i].DefaultPan = pack_getc(f); j->Instrument[i].RandomVolume = pack_getc(f); j->Instrument[i].RandomPanning = pack_getc(f); #ifdef DEBUG_INSTRUMENTS fprintf(stderr, "I%02i @ 0x%X, NNA %i, DCT %i, DCA %i, FO %i, " "PPS %i, PPC %i, GVol %i, DPan %i, RV %i, RP %i\n", i, insoffs[i], j->Instrument[i].NewNoteAction, j->Instrument[i].DuplicateCheckType, j->Instrument[i].DuplicateCheckAction, j->Instrument[i].FadeOut, j->Instrument[i].PitchPanSeparation, j->Instrument[i].PitchPanCenter, j->Instrument[i].GlobalVolume, j->Instrument[i].DefaultPan, j->Instrument[i].RandomVolume, j->Instrument[i].RandomPanning); #endif /* Skip Tracker Version and Number of Samples. These are only used in * separate instrument files. Also skip unused byte and Instrument Name. */ pack_fseek(f, 30); j->Instrument[i].FilterCutOff = pack_getc(f); j->Instrument[i].FilterResonance = pack_getc(f); /* Skip MIDI Channel, Program and Bank */ pack_fseek(f, 4); for (k = 0; k < 120; k++) { j->Instrument[i].NoteNote[k] = pack_getc(f); j->Instrument[i].NoteSample[k] = pack_getc(f); } read_envelopes(f, j, i); pack_fclose(f); if (errno) { destroy_it(j); return NULL; } return j; } MODULE *load_it_sample(char *file, long offset, MODULE *j, int i) { int sam_samptr, convert; int len; int k; SAMPLE *sam; void *dat; PACKFILE *f = pack_fopen(file, F_READ); if (!f) { #ifdef DEBUG_SAMPLES fprintf(stderr, "Error opening!\n"); #endif destroy_it(j); return NULL; } pack_fseek(f, offset); if (pack_mgetl(f) != AL_ID('I','M','P','S')) { destroy_it(j); return NULL; } /* Skip DOS Filename. */ pack_fseek(f, 13); j->Sample[i].GlobalVolume = pack_getc(f); j->Sample[i].Flag = pack_getc(f); j->Sample[i].DefaultVolume = pack_getc(f); #ifdef DEBUG_SAMPLES fprintf(stderr, "S%02i @ 0x%X, Vol: %i/%i, Flag: %i", i, samoffs[i], j->Sample[i].GlobalVolume, j->Sample[i].Volume, j->Sample[i].Flag); #endif /* Skip Sample Name. */ pack_fseek(f, 26); convert = pack_getc(f); j->Sample[i].DefaultPanning = pack_getc(f); j->Sample[i].SampleLength = pack_igetl(f); j->Sample[i].LoopBegin = pack_igetl(f); j->Sample[i].LoopEnd = pack_igetl(f); j->Sample[i].C5Speed = pack_igetl(f); j->Sample[i].SustainLoopBegin = pack_igetl(f); j->Sample[i].SustainLoopEnd = pack_igetl(f); #ifdef DEBUG_SAMPLES fprintf(stderr, ", SLen: %i, LpB: %i, LpE: %i, C5S: %i\n", j->Sample[i].SampleLength, j->Sample[i].LoopBegin, j->Sample[i].LoopEnd, j->Sample[i].C5Speed); #endif sam_samptr = pack_igetl(f); j->Sample[i].VibratoSpeed = pack_getc(f); j->Sample[i].VibratoDepth = pack_getc(f); j->Sample[i].VibratoRate = pack_getc(f); j->Sample[i].VibratoWaveForm = pack_getc(f); #ifdef DEBUG_SAMPLES fprintf(stderr, "SusLpB: %i, SusLpE: %i, VibSp: %i, VibDep: %i, VibWav: %i, VibRat: %i\n", j->Sample[i].SustainLoopBegin, j->Sample[i].SustainLoopEnd, j->Sample[i].VibratoSpeed, j->Sample[i].VibratoDepth, j->Sample[i].VibratoWaveForm, j->Sample[i].VibratoRate); #endif pack_fclose(f); if (errno) { destroy_it(j); return NULL; } if (j->Sample[i].Flag & SAMPLE_HASSAMPLE) { f = pack_fopen(file, F_READ); pack_fseek(f, sam_samptr); len = j->Sample[i].SampleLength; if (j->Sample[i].Flag & SAMPLE_16BIT) len <<= 1; #ifndef STEREO_SAMPLES_COUNT_AS_TWO if (j->Sample[i].Flag & SAMPLE_STEREO) len <<= 1; #endif #ifdef DEBUG_SAMPLES fprintf(stderr, "Len: %i, Size: %i KB\n", j->Sample[i].SampleLength, len/1024); #endif sam = create_sample(j->Sample[i].Flag & SAMPLE_16BIT ? 16 : 8, j->Sample[i].Flag & SAMPLE_STEREO ? 1 : 0, #ifdef STEREO_SAMPLES_COUNT_AS_TWO j->Sample[i].C5Speed >> 1, #else j->Sample[i].C5Speed, #endif j->Sample[i].SampleLength); if (j->Sample[i].Flag & 8) { // If the sample is packed, then we must unpack it if (j->Sample[i].Flag & SAMPLE_16BIT) decompress16(f, sam->data, j->Sample[i].SampleLength, j->Version); else decompress8(f, sam->data, j->Sample[i].SampleLength, j->Version); } else if ((j->Sample[i].Flag & SAMPLE_16BIT)) { if (convert & 2) for (k = 0; k < len; k += 2) *(short *)((char *)sam->data + k) = pack_mgetw(f); else for (k = 0; k < len; k += 2) *(short *)((char *)sam->data + k) = pack_igetw(f); } else { pack_fread(sam->data, len, f); } if (j->Sample[i].Flag & SAMPLE_USELOOP) { sam->loop_start = j->Sample[i].LoopBegin; sam->loop_end = j->Sample[i].LoopEnd; } j->Sample[i].Sample = sam; dat = sam->data; if (convert & 1) { /* Convert to unsigned. */ if (sam->bits == 8) for (k = 0; k < len; k++) ((char *)dat)[k] ^= 0x80; else for (k = 0; k < len; k += 2) *(short *)((char *)dat + k) ^= 0x8000; } /* NOT SUPPORTED: * * convert & 4 - Samples stored as delta values * convert & 8 - Samples stored as byte delta values * convert & 16 - Samples stored as TX-Wave 12-bit values * convert & 32 - Left/Right/All Stereo prompt */ pack_fclose(f); if (errno) { destroy_it(j); return NULL; } } return j; } MODULE *load_pattern(char *file, long offset, MODULE *j, int i) { unsigned char *buf; unsigned char cmask[64], cnote[64], cinstrument[64], cvol[64], ccom[64], ccomval[64]; int numnotes = 0, len, pos = 0, mask = 0, chn = 0; PACKFILE *f; if (offset == 0) { /* Empty 64-row pattern. */ j->Pattern[i].NumRows = 64; j->Pattern[i].NumNotes = 0; return j; } f = pack_fopen(file, F_READ); if (!f) { destroy_it(j); return NULL; } buf = malloc(65536); memset(cmask, 0, 64); memset(cnote, 0, 64); memset(cinstrument, 0, 64); memset(cvol, 0, 64); memset(ccom, 0, 64); memset(ccomval, 0, 64); pack_fseek(f, offset); len = pack_igetw(f); j->Pattern[i].NumRows = pack_igetw(f); /* Skip four unused bytes. */ pack_fseek(f, 4); pack_fread(buf, len, f); while (pos < len) { int b = buf[pos++]; if (!b) { /* End of row. */ numnotes++; continue; } chn = (b - 1) & 63; if (b & 128) { mask = buf[pos]; pos++; cmask[chn] = mask; } else mask = cmask[chn]; if (mask) numnotes++; if (mask & 1) pos++; if (mask & 2) pos++; if (mask & 4) pos++; if (mask & 8) pos += 2; } j->Pattern[i].NumNotes = numnotes; j->Pattern[i].Note = malloc(numnotes * sizeof(MODULE_NOTE)); memset(j->Pattern[i].Note, 0, numnotes * sizeof(MODULE_NOTE)); pos = 0; memset(cmask, 0, 64); mask = 0; numnotes = 0; while (pos < len) { int b = buf[pos]; #ifdef DEBUG_PATTERNS fprintf(stderr, "NumNote: %i ", numnotes); #endif pos++; if (b == 0) { //If end of row: j->Pattern[i].Note[numnotes].Channel = -1; numnotes++; #ifdef DEBUG_PATTERNS fprintf(stderr, "Channel: -1\n"); #endif continue; } chn = (b - 1) & 63; if (b & 128) { mask = buf[pos]; pos++; cmask[chn] = mask; } else mask = cmask[chn]; #ifdef DEBUG_PATTERNS fprintf(stderr, "Channel: %i Mask: %i ", chn, mask); #endif if (mask) j->Pattern[i].Note[numnotes].Channel = chn; if (mask & 1) { j->Pattern[i].Note[numnotes].Note = buf[pos]; j->Pattern[i].Note[numnotes].Mask |= NOTEMASK_NOTE; cnote[chn] = buf[pos]; #ifdef DEBUG_PATTERNS fprintf(stderr, "Note: %i ", buf[pos]); #endif pos++; } if (mask & 2) { j->Pattern[i].Note[numnotes].Instrument = buf[pos]; j->Pattern[i].Note[numnotes].Mask |= NOTEMASK_INSTRUMENT; cinstrument[chn] = buf[pos]; #ifdef DEBUG_PATTERNS fprintf(stderr, "Inst: %i ", buf[pos]); #endif pos++; } if (mask & 4) { j->Pattern[i].Note[numnotes].VolPan = buf[pos]; j->Pattern[i].Note[numnotes].Mask |= NOTEMASK_VOLPAN; cvol[chn] = buf[pos]; #ifdef DEBUG_PATTERNS fprintf(stderr, "Vol: %i ", buf[pos]); #endif pos++; } if (mask & 8) { j->Pattern[i].Note[numnotes].Command = buf[pos]; j->Pattern[i].Note[numnotes].CommandValue = buf[pos+1]; j->Pattern[i].Note[numnotes].Mask |= NOTEMASK_COMMAND; ccom[chn] = buf[pos]; ccomval[chn] = buf[pos+1]; #ifdef DEBUG_PATTERNS fprintf(stderr, "Com: %i CommArg: %i ", buf[pos], buf[pos+1]); #endif pos += 2; } if (mask & 16) { j->Pattern[i].Note[numnotes].Note = cnote[chn]; j->Pattern[i].Note[numnotes].Mask |= NOTEMASK_NOTE; #ifdef DEBUG_PATTERNS fprintf(stderr, "LNote: %i ", cnote[chn]); #endif } if (mask & 32) { j->Pattern[i].Note[numnotes].Instrument = cinstrument[chn]; j->Pattern[i].Note[numnotes].Mask |= NOTEMASK_INSTRUMENT; #ifdef DEBUG_PATTERNS fprintf(stderr, "LInst: %i ", cinstrument[chn]); #endif } if (mask & 64) { j->Pattern[i].Note[numnotes].VolPan = cvol[chn]; j->Pattern[i].Note[numnotes].Mask |= NOTEMASK_VOLPAN; #ifdef DEBUG_PATTERNS fprintf(stderr, "LVol: %i ", cvol[chn]); #endif } if (mask & 128) { j->Pattern[i].Note[numnotes].Command = ccom[chn]; j->Pattern[i].Note[numnotes].CommandValue = ccomval[chn]; j->Pattern[i].Note[numnotes].Mask |= NOTEMASK_COMMAND; #ifdef DEBUG_PATTERNS fprintf(stderr, "LCom: %i LComArg: %i ", ccom[chn], ccomval[chn]); #endif } #ifdef DEBUG_PATTERNS fprintf(stderr, "\n"); #endif if (mask) numnotes++; #ifdef DEBUG_PATTERNS rest(1000); #endif } free(buf); pack_fclose(f); if (errno) { destroy_it(j); return NULL; } return j; } MODULE *load_it(char *file) { PACKFILE *f; MODULE *j = create_it(); int tver, tver2, flag; long *insoffs = NULL, *samoffs = NULL, *patoffs = NULL; int i; if (!j) return NULL; f = pack_fopen(file, F_READ); if (!f) return NULL; if (pack_mgetl(f) != MUSIC_IT) { pack_fclose(f); return NULL; } /* Skip song name and pattern row highlight info */ pack_fseek(f, 28); j->NumOrders = pack_igetw(f); j->NumInstruments = pack_igetw(f); j->NumSamples = pack_igetw(f); j->NumPatterns = pack_igetw(f); #ifdef DEBUG_HEADER fprintf(stderr, "Loading IT: %i Orders, %i Instruments, %i Samples, %i Patterns\n", j->NumOrders, j->NumInstruments, j->NumSamples, j->NumPatterns); #endif tver = pack_igetw(f); j->Version = tver2 = pack_igetw(f); #ifdef DEBUG_HEADER fprintf(stderr, "Tracker ver: %X, %X\n", tver, tver2); #endif j->Flags = pack_igetw(f); flag = pack_igetw(f); j->GlobalVolume = pack_getc(f); j->MixVolume = pack_getc(f); j->Speed = pack_getc(f); j->Tempo = pack_getc(f); j->PanningSeparation = pack_getc(f); #ifdef DEBUG_HEADER fprintf(stderr, "Global Volume: %i, Mixing Volume: %i, Speed: %i, Tempo: %i, PanSep: %i\n", j->GlobalVolume, j->MixVolume, j->Speed, j->Tempo, j->PanningSeparation); #endif /* Skip Pitch Wheel Depth, Message Length, Message Offset and Reserved */ pack_fseek(f, 11); #ifdef DEBUG_HEADER fprintf(stderr, "Channel Pan:"); #endif for (i = 0; i < MAX_IT_CHN; i++) { j->Channel[i].Pan = pack_getc(f); #ifdef DEBUG_HEADER fprintf(stderr, " %i", j->Channel[i].Pan); #endif } #ifdef DEBUG_HEADER fprintf(stderr, "\nChannel Vol:"); #endif for (i = 0; i < MAX_IT_CHN; i++) { j->Channel[i].ChannelVolume = pack_getc(f); #ifdef DEBUG_HEADER fprintf(stderr, " %i", j->Channel[i].ChannelVolume); #endif } #ifdef DEBUG_HEADER fprintf(stderr, "\n"); #endif j->Order = malloc(j->NumOrders); if (!j->Order) { destroy_it(j); return NULL; } pack_fread(j->Order, j->NumOrders, f); /* Whoops, no error checking! Er, would it be better to use arrays? */ if (j->NumInstruments) insoffs = malloc(j->NumInstruments * sizeof(*insoffs)); if (j->NumSamples) samoffs = malloc(j->NumSamples * sizeof(*samoffs)); if (j->NumPatterns) patoffs = malloc(j->NumPatterns * sizeof(*patoffs)); for (i = 0; i < j->NumInstruments; i++) insoffs[i] = pack_igetl(f); for (i = 0; i < j->NumSamples; i++) samoffs[i] = pack_igetl(f); for (i = 0; i < j->NumPatterns; i++) patoffs[i] = pack_igetl(f); /* No skipping necessary - we can use the offsets. if (flag&1) { //Song message attached //Ignore. } if (flag & 4) { //skip something: short u; char dummy[8]; int i; u = pack_igetw(f); for (i=0; iNumInstruments) { j->Instrument = malloc(j->NumInstruments * sizeof(MODULE_INSTRUMENT)); if (!j->Instrument) { #ifdef DEBUG_INSTRUMENTS fprintf(stderr, "No Mem for Instruments!\n"); #endif free(insoffs); free(samoffs); free(patoffs); destroy_it(j); return NULL; } memset(j->Instrument, 0, j->NumInstruments * sizeof(MODULE_INSTRUMENT)); for (i = 0; i < j->NumInstruments; i++) { if (tver2 < 0x200) j = load_old_instrument(file, insoffs[i], j, i); else j = load_instrument(file, insoffs[i], j, i); if (!j) { free(insoffs); free(samoffs); free(patoffs); return NULL; } } } if (j->NumSamples) { j->Sample = malloc(j->NumSamples * sizeof(MODULE_SAMPLE)); if (!j->Sample) { #ifdef DEBUG_SAMPLES fprintf(stderr, "No Mem for Samples!\n"); #endif free(insoffs); free(samoffs); free(patoffs); destroy_it(j); return NULL; } memset(j->Sample, 0, j->NumSamples * sizeof(MODULE_SAMPLE)); for (i=0; iNumSamples; i++) { j = load_it_sample(file, samoffs[i], j, i); if (!j) { free(insoffs); free(samoffs); free(patoffs); return NULL; } } } if (j->NumPatterns) { j->Pattern = malloc(j->NumPatterns * sizeof(MODULE_PATTERN)); if (!j->Pattern) { #ifdef DEBUG_PATTERNS fprintf(stderr, "No Mem for Patterns!\n"); #endif free(insoffs); free(samoffs); free(patoffs); destroy_it(j); return NULL; } memset(j->Pattern, 0, j->NumPatterns * sizeof(MODULE_PATTERN)); for (i = 0; i < j->NumPatterns; i++) { j = load_pattern(file, patoffs[i], j, i); if (!j) { free(insoffs); free(samoffs); free(patoffs); return NULL; } } } if (insoffs) free(insoffs); if (samoffs) free(samoffs); if (patoffs) free(patoffs); return j; } /* ----------------- */ #define DUH_SIGNATURE AL_ID('D', 'U', 'H', '!') #define SIGTYPE_SAMPLE AL_ID('S', 'A', 'M', 'P') #define SAMPFLAG_16BIT 1 /* sample in file is 16 bit, rather than 8 bit */ #define SAMPFLAG_LOOP 2 /* loop indefinitely */ #define SAMPFLAG_XLOOP 4 /* loop x times; only relevant if LOOP not set */ #define SAMPFLAG_PINGPONG 8 /* loop back and forth, if LOOP or XLOOP set */ /* SAMPPARAM_N_LOOPS: add 'value' iterations to the loop. 'value' is assumed * to be positive. */ #define SAMPPARAM_N_LOOPS 0 #define SIGTYPE_COMBINING AL_ID('C', 'O', 'M', 'B') #define SIGTYPE_STEREOPAN AL_ID('S', 'P', 'A', 'N') #define SPANPARAM_PAN 0 #define SIGTYPE_SEQUENCE AL_ID('S', 'E', 'Q', 'U') #define SEQUENCE_START_SIGNAL 0 #define SEQUENCE_SET_VOLUME 1 #define SEQUENCE_SET_PITCH 2 #define SEQUENCE_SET_PARAMETER 3 #define SEQUENCE_STOP_SIGNAL 4 void write_sample(MODULE_SAMPLE *sample, PACKFILE *f, int channel) { long size = sample->SampleLength; long i; int flags = 0; long loop_start = 0, loop_end = 0; SAMPLE *smp = sample->Sample; pack_mputl(SIGTYPE_SAMPLE, f); #ifdef STEREO_SAMPLES_COUNT_AS_TWO if (sample->Flag & SAMPLE_STEREO) { ASSERT((size & 1) == 0); size >>= 1; } #endif #ifdef SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP if (sample->Flag & SAMPLE_USESUSTAINLOOP) { flags |= SAMPFLAG_XLOOP; if (sample->Flag & SAMPLE_PINGPONGSUSTAINLOOP) flags |= SAMPFLAG_PINGPONG; } else if (sample->Flag & SAMPLE_USELOOP) { flags |= SAMPFLAG_LOOP; if (sample->Flag & SAMPLE_PINGPONGLOOP) flags |= SAMPFLAG_PINGPONG; } #else if (sample->Flag & SAMPLE_USELOOP) { flags |= SAMPFLAG_LOOP; if (sample->Flag & SAMPLE_PINGPONGLOOP) flags |= SAMPFLAG_PINGPONG; } else if (sample->Flag & SAMPLE_USESUSTAINLOOP) { flags |= SAMPFLAG_XLOOP; if (sample->Flag & SAMPLE_PINGPONGSUSTAINLOOP) flags |= SAMPFLAG_PINGPONG; } #endif if (flags & (SAMPFLAG_LOOP | SAMPFLAG_XLOOP)) { if (flags & SAMPFLAG_LOOP) { loop_start = sample->LoopBegin; loop_end = sample->LoopEnd; } else { loop_start = sample->SustainLoopBegin; loop_end = sample->SustainLoopEnd; } #ifdef STEREO_SAMPLES_COUNT_AS_TWO if (sample->Flag & SAMPLE_STEREO) { ASSERT(((loop_start | loop_end) & 1) == 0); loop_start >>= 1; loop_end >>= 1; } #endif if (loop_end > size) loop_end = size; else if (flags & SAMPFLAG_LOOP) size = loop_end; } pack_iputl(size, f); if (smp->bits == 16) flags |= SAMPFLAG_16BIT; pack_putc(flags, f); if (flags & (SAMPFLAG_LOOP | SAMPFLAG_XLOOP)) { pack_iputl(loop_start, f); if (!(flags & SAMPFLAG_LOOP)) pack_iputl(loop_end, f); } if (flags & SAMPFLAG_16BIT) { if (smp->stereo) { for (i = 0; i < size; i++) pack_iputw(((unsigned short *)smp->data)[(i << 1) + channel] ^ 0x8000, f); } else { for (i = 0; i < size; i++) pack_iputw(((unsigned short *)smp->data)[i] ^ 0x8000, f); } } else { if (smp->stereo) { /* TEMPORARY CHANNEL COMBINING */ for (i = 0; i < size; i++) pack_putc(((unsigned char *)smp->data)[(i << 1) + channel] ^ 0x80, f); } else { for (i = 0; i < size; i++) pack_putc(((unsigned char *)smp->data)[i] ^ 0x80, f); } } } int sample_is_valid(MODULE_SAMPLE *sample) { if (!(sample->Flag & SAMPLE_HASSAMPLE)) return 0; if (sample->SampleLength <= 0) return 0; #ifdef SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP if ((sample->Flag & (SAMPLE_USELOOP | SAMPLE_USESUSTAINLOOP)) == SAMPLE_USELOOP) #else if (sample->Flag & SAMPLE_USELOOP) #endif if (sample->LoopEnd <= 0) return 0; return 1; } #define first_sample 1 void write_mono_sample(MODULE_SAMPLE *sample, int n_samples, PACKFILE *f) { pack_mputl(SIGTYPE_STEREOPAN, f); /* Refer to the next sample. */ pack_iputl(first_sample + n_samples + 1, f); write_sample(sample, f, 0); } void write_stereo_sample(MODULE_SAMPLE *sample, int n_samples, PACKFILE *f) { pack_mputl(SIGTYPE_COMBINING, f); /* Two samples. */ pack_putc(2, f); /* Refer to the next two samples. */ pack_iputl(first_sample + n_samples + 1, f); pack_iputl(first_sample + n_samples + 2, f); write_sample(sample, f, 0); write_sample(sample, f, 1); } unsigned char *seqdata = NULL; int seqpos = 0; int max_seqdata = 0; int seqtime = 0; int songtime; void sequence_c(int v) { if (seqpos >= max_seqdata) { max_seqdata += 1024; seqdata = realloc(seqdata, max_seqdata); if (!seqdata) { errno = ENOMEM; return; } } seqdata[seqpos++] = v; } void sequence_w(int v) { sequence_c(v); sequence_c(v >> 8); } void sequence_l(long v) { sequence_c(v); sequence_c(v >> 8); sequence_c(v >> 16); sequence_c(v >> 24); } void sequence_cl(unsigned long v) { int byte_count = 0; while (byte_count < 4 && v >= 128) { sequence_c(v | 128); v >>= 7; byte_count++; } sequence_c(v & 127); } void write_seqtime() { sequence_cl(seqtime); seqtime = 0; } int *sample_signal = NULL; void calculate_pan(MODULE *module, MODULE_CHANNEL *channel) { int samp; if (module->Flags & FLAG_USEINSTRUMENTS && channel->Instrument >= 1 && channel->Instrument <= module->NumInstruments) { MODULE_INSTRUMENT *instrument = &module->Instrument[(int)channel->Instrument - 1]; if (instrument) { unsigned char note = instrument->NoteNote[channel->Note]; samp = instrument->NoteSample[channel->Note]; if (instrument->DefaultPan <= 64) channel->Pan = instrument->DefaultPan; channel->PanSep = (note - instrument->PitchPanCenter) * instrument->PitchPanSeparation / 8; } else { samp = 0; channel->PanSep = 0; } } else { samp = channel->Instrument; channel->PanSep = 0; } if (samp >= 1 && samp <= module->NumSamples) { int pan = module->Sample[samp - 1].DefaultPanning; if (pan >= 128 && pan <= 192) channel->Pan = pan - 128; } } /* This function assumes note->Channel >= 0. */ void process_note_data(MODULE *module, MODULE_NOTE *note) { MODULE_CHANNEL *channel = &module->Channel[(int)note->Channel]; if (note->Mask & NOTEMASK_NOTE) { if (note->Note >= 120) { channel->Note = note->Note; } else if (channel->Note >= 120) { channel->Note = note->Note; channel->Flag |= CHANNEL_RETRIG; } else { channel->Note = note->Note; if ((note->Mask & NOTEMASK_VOLPAN) == 0 || !(note->VolPan >= 193 && note->VolPan <= 202)) { if ((note->Mask & NOTEMASK_COMMAND) == 0 || (note->Command != IT_TONE_PORTAMENTO && note->Command != IT_VOLSLIDE_TONEPORTA)) { channel->Flag |= CHANNEL_RETRIG; } } } } if (note->Mask & NOTEMASK_INSTRUMENT) { if (note->Instrument != channel->Instrument) { channel->Flag |= CHANNEL_RETRIG; channel->Instrument = note->Instrument; if (!(module->Flags & FLAG_USEINSTRUMENTS)) { if (channel->Instrument >= 1 && channel->Instrument <= module->NumSamples) channel->sample = &module->Sample[channel->Instrument - 1]; else channel->sample = NULL; } } } if (module->Flags & FLAG_USEINSTRUMENTS) { if (channel->Instrument >= 1 && channel->Instrument <= module->NumInstruments) { int samp = module->Instrument[(int)channel->Instrument - 1].NoteSample[channel->Note]; if (samp >= 1 && samp <= module->NumSamples) channel->sample = &module->Sample[samp - 1]; else channel->sample = NULL; } else channel->sample = NULL; } if (channel->Flag & CHANNEL_RETRIG) calculate_pan(module, channel); if (note->Mask & NOTEMASK_INSTRUMENT) if (channel->sample) channel->Volume = channel->sample->DefaultVolume; if (!channel->sample) channel->Flag &= ~CHANNEL_RETRIG; if (note->Mask & NOTEMASK_VOLPAN) { if (note->VolPan <= 64) channel->Volume = note->VolPan; else if (note->VolPan <= 74) { unsigned char v = note->VolPan - 65; if (v == 0) v = channel->lastvolslide; channel->lastvolslide = v; /* = effect DxF where x == note->VolPan - 65 */ channel->Volume += v; if (channel->Volume > 64) channel->Volume = 64; } else if (note->VolPan <= 84) { unsigned char v = note->VolPan - 75; if (v == 0) v = channel->lastvolslide; channel->lastvolslide = v; /* = effect DFx where x == note->VolPan - 75 */ channel->Volume -= v; if (channel->Volume > 64) channel->Volume = 0; } else if (note->VolPan <= 94) { unsigned char v = note->VolPan - 85; if (v == 0) v = channel->lastvolslide; channel->lastvolslide = v; /* = effect Dx0 where x == note->VolPan - 85 */ channel->volslide = v; } else if (note->VolPan <= 104) { unsigned char v = note->VolPan - 95; if (v == 0) v = channel->lastvolslide; channel->lastvolslide = v; /* = effect D0x where x == note->VolPan - 95 */ channel->volslide = -v; } else if (note->VolPan <= 114) { unsigned char v = (note->VolPan - 105) << 2; if (v == 0) v = channel->lastEF; channel->lastEF = v; channel->portamento -= v << 4; } else if (note->VolPan <= 124) { unsigned char v = (note->VolPan - 115) << 2; if (v == 0) v = channel->lastEF; channel->lastEF = v; channel->portamento += v << 4; } else if (note->VolPan < 128) { } else if (note->VolPan <= 192) channel->Pan = note->VolPan - 128; else if (note->VolPan <= 202) { //Tone Portamento /* Affects G's memory. Has the equivalent slide given by this * table: */ static unsigned char SlideTable[] = {0, 1, 4, 8, 16, 32, 64, 96, 128, 255}; unsigned char v = SlideTable[note->VolPan - 193]; if (module->Flags & FLAG_COMPATIBLEGXX) { if (v == 0) v = channel->lastG; channel->lastG = v; } else { if (v == 0) v = channel->lastEF; channel->lastEF = v; } channel->toneporta += v << 4; } else if (note->VolPan <= 212) ;//Vibrato /* This uses the same 'memory' as Hxx/Uxx. */ } if (note->Mask & NOTEMASK_COMMAND) { //Interpret effect //unsigned char note->Command, note->CommandValue switch (note->Command) { /* Notes about effects (as compared to other module formats) C This is now in *HEX*. (Used to be in decimal in ST3) E/F/G/H/U You need to check whether the song uses Amiga/Linear slides. H/U Vibrato in Impulse Tracker is two times finer than in any other tracker and is updated EVERY tick. If "Old Effects" is *ON*, then the vibrato is played in the normal manner (every non-row tick and normal depth) E/F/G These commands ALL share the same memory. Oxx Offsets to samples are to the 'xx00th' SAMPLE. (ie. for 16 bit samples, the offset is xx00h*2) Oxx past the sample end will be ignored, unless "Old Effects" is ON, in which case the Oxx will play from the end of the sample. Yxy This uses a table 4 times larger (hence 4 times slower) than vibrato or tremelo. If the waveform is set to random, then the 'speed' part of the command is interpreted as a delay. */ case IT_SET_SPEED: if (note->CommandValue) {module->tick = module->Speed = note->CommandValue;} break; //case IT_JUMP_TO_ORDER: module->processorder = note->CommandValue - 1; module->processrow = 0xFFFE; break; //IT_JUMP_TO_ORDER would most likely be used to jump back, so it must be handled specially. case IT_BREAK_TO_ROW: module->breakrow = note->CommandValue; module->processrow = 0xFFFE; break; case IT_VOLUME_SLIDE: { unsigned char v = note->CommandValue; if (v == 0) v = channel->lastDKL; channel->lastDKL = v; if ((v & 0x0F) == 0) { /* Dx0 */ channel->volslide = v >> 4; if (channel->volslide == 15) { channel->Volume += 15; if (channel->Volume > 64) channel->Volume = 64; } } else if ((v & 0xF0) == 0) { /* D0x */ channel->volslide = -v; if (channel->volslide == -15) { channel->Volume -= 15; if (channel->Volume > 64) channel->Volume = 0; } } else if ((v & 0x0F) == 0x0F) { /* DxF */ channel->Volume += v >> 4; if (channel->Volume > 64) channel->Volume = 64; } else if ((v & 0xF0) == 0xF0) { /* DFx */ channel->Volume -= v & 15; if (channel->Volume > 64) channel->Volume = 0; } } break; case IT_PORTAMENTO_DOWN: { unsigned char v = note->CommandValue; if (v == 0) v = channel->lastEF; channel->lastEF = v; channel->portamento -= v << 4; } break; case IT_PORTAMENTO_UP: { unsigned char v = note->CommandValue; if (v == 0) v = channel->lastEF; channel->lastEF = v; channel->portamento += v << 4; } break; case IT_TONE_PORTAMENTO: { unsigned char v = note->CommandValue; if (module->Flags & FLAG_COMPATIBLEGXX) { if (v == 0) v = channel->lastG; channel->lastG = v; } else { if (v == 0) v = channel->lastEF; channel->lastEF = v; } channel->toneporta += v << 4; } break; //case IT_VIBRATO: //case IT_TREMOR: //case IT_ARPEGGIO: //case IT_VOLSLIDE_VIBRATO: //case IT_VOLSLIDE_TONEPORTA: case IT_SET_CHANNEL_VOLUME: if (note->CommandValue <= 64) channel->ChannelVolume = note->CommandValue; #ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM else channel->ChannelVolume = 64; #endif break; case IT_CHANNEL_VOLUME_SLIDE: { unsigned char v = note->CommandValue; if (v == 0) v = channel->lastN; channel->lastN = v; if ((v & 0x0F) == 0) { /* Nx0 */ channel->channelvolslide = v >> 4; } else if ((v & 0xF0) == 0) { /* N0x */ channel->channelvolslide = -v; } else if ((v & 0x0F) == 0x0F) { /* NxF */ channel->ChannelVolume += v >> 4; if (channel->ChannelVolume > 64) channel->ChannelVolume = 64; } else if ((v & 0xF0) == 0xF0) { /* NFx */ channel->ChannelVolume -= v & 15; if (channel->ChannelVolume > 64) channel->ChannelVolume = 0; } } break; //case IT_SET_SAMPLE_OFFSET: //case IT_PANNING_SLIDE: //case IT_RETRIGGER_NOTE: //case IT_TREMOLO: case IT_S: switch (note->CommandValue >> 4) { //case IT_S_SET_FILTER: //case IT_S_SET_GLISSANDO_CONTROL: //case IT_S_FINETUNE: //case IT_S_SET_VIBRATO_WAVEFORM: //case IT_S_SET_TREMOLO_WAVEFORM: //case IT_S_SET_PANBRELLO_WAVEFORM: /* Waveforms for commands S3x, S4x and S5x: * 0: Sine wave * 1: Ramp down * 2: Square wave * 3: Random wave */ //case IT_S7: case IT_S_SET_PAN: channel->Pan = ((note->CommandValue & 15) << 2) | ((note->CommandValue & 15) >> 2); break; //case IT_S_SET_SURROUND_SOUND: /* S91 Set surround sound */ //case IT_S_SET_HIGH_OFFSET: /* SAy Set high value of sample offset yxx00h */ //case IT_S_PATTERN_LOOP: /* SB0 Set loopback point */ //case IT_S_DELAYED_NOTE_CUT: //case IT_S_NOTE_DELAY: /* SEx Pattern delay for x rows */ //case IT_S_SET_MIDI_MACRO: } break; case IT_SET_SONG_TEMPO: { unsigned char v = note->CommandValue; if (v == 0) v = channel->lastW; channel->lastW = v; if (v < 0x10) module->temposlide = -v; else if (v < 0x20) module->temposlide = v & 15; else module->Tempo = v; } break; //case IT_FINE_VIBRATO: case IT_SET_GLOBAL_VOLUME: if (note->CommandValue <= 128) module->GlobalVolume = note->CommandValue; #ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM else module->GlobalVolume = 128; #endif break; case IT_GLOBAL_VOLUME_SLIDE: { unsigned char v = note->CommandValue; if (v == 0) v = channel->lastW; channel->lastW = v; if ((v & 0x0F) == 0) { /* Nx0 */ module->globalvolslide = v >> 4; } else if ((v & 0xF0) == 0) { /* N0x */ module->globalvolslide = -v; } else if ((v & 0x0F) == 0x0F) { /* NxF */ module->GlobalVolume += v >> 4; if (module->GlobalVolume > 64) module->GlobalVolume = 64; } else if ((v & 0xF0) == 0xF0) { /* NFx */ module->GlobalVolume -= v & 15; if (module->GlobalVolume > 64) module->GlobalVolume = 0; } } break; case IT_SET_PANNING: channel->Pan = (note->CommandValue + 2) >> 2; break; //case IT_PANBRELLO: //case IT_MIDI_MACRO://see MIDI.TXT } } } void update_effects(MODULE *module) { int i; module->GlobalVolume += module->globalvolslide; if (module->GlobalVolume > 128) { if (module->globalvolslide >= 0) module->GlobalVolume = 128; else module->GlobalVolume = 0; } module->Tempo += module->temposlide; if (module->Tempo < 32) { if (module->temposlide >= 0) module->Tempo = 255; else module->Tempo = 32; } for (i = 0; i < 64; i++) { MODULE_CHANNEL *channel = &module->Channel[i]; channel->Volume += channel->volslide; if (channel->Volume > 64) { if (channel->volslide >= 0) channel->Volume = 64; else channel->Volume = 0; } channel->ChannelVolume += channel->channelvolslide; if (channel->ChannelVolume > 64) { if (channel->channelvolslide >= 0) channel->ChannelVolume = 64; else channel->ChannelVolume = 0; } channel->pitch += channel->portamento; //We do not enforce any limits here. IT surely does. Investigate. if (channel->toneporta && channel->sample) { int destpitch; if (module->Flags & FLAG_USEINSTRUMENTS) destpitch = module->Instrument[(int)channel->Instrument - 1].NoteNote[channel->Note]; else destpitch = channel->Note; destpitch = (destpitch - 60) << 8; if (channel->pitch > destpitch) { channel->pitch -= channel->toneporta; if (channel->pitch < destpitch) channel->pitch = destpitch; } else if (channel->pitch < destpitch) { channel->pitch += channel->toneporta; if (channel->pitch > destpitch) channel->pitch = destpitch; } } } } void reset_effects(MODULE *module) { int i; module->globalvolslide = 0; module->temposlide = 0; for (i = 0; i < 64; i++) { module->Channel[i].volslide = 0; module->Channel[i].channelvolslide = 0; module->Channel[i].portamento = 0; module->Channel[i].toneporta = 0; } } /* This function assumes note->Channel >= 0... or it might later. */ void update_pattern_variables(MODULE *module, MODULE_NOTE *note) { /* MODULE_CHANNEL *channel = &module->Channel[(int)note->Channel]; */ if (note->Mask & NOTEMASK_COMMAND) { switch (note->Command) { case IT_S: switch (note->CommandValue >> 4) { case IT_S_FINE_PATTERN_DELAY: module->tick = module->Speed + (note->CommandValue & 15); break; //case IT_S7: //case IT_S_PATTERN_LOOP: /* SB0 Set loopback point */ //case IT_S_DELAYED_NOTE_CUT: //case IT_S_NOTE_DELAY: case IT_S_PATTERN_DELAY: module->rowcount = 1 + (note->CommandValue & 15); break; } break; } } } unsigned char envelope_get_y(MODULE_ENVELOPE *envelope, MODULE_VENVELOPE *venvelope) { int ys, ye; int ts, te; int t; if (venvelope->NextNode <= 0) return envelope->NodeY[0]; if (venvelope->NextNode >= envelope->NumNodes) return envelope->NodeY[envelope->NumNodes - 1]; ys = envelope->NodeY[venvelope->NextNode - 1]; ye = envelope->NodeY[venvelope->NextNode]; ts = envelope->NodeTick[venvelope->NextNode - 1]; te = envelope->NodeTick[venvelope->NextNode]; t = venvelope->CurTick; if (ts == te) return ys; return ys + (ye - ys) * (t - ts) / (te - ts); } unsigned int calculate_volume(MODULE *module, MODULE_VCHANNEL *vchannel) { if (module->Flags & FLAG_USEINSTRUMENTS) { int vev = 64; if (module->Instrument[vchannel->Instrument - 1].VolumeEnvelope.Flag & ENVELOPE_ON) vev = envelope_get_y(&module->Instrument[vchannel->Instrument - 1].VolumeEnvelope, &vchannel->VVolEnv); return (((((((vchannel->Volume * vchannel->sample->GlobalVolume * module->Instrument[vchannel->Instrument - 1].GlobalVolume) >> 7) * module->GlobalVolume) >> 11) * vev * module->MixVolume) >> 10) * vchannel->fadeoutcount) >> 11; } return (((vchannel->Volume * vchannel->sample->GlobalVolume * module->GlobalVolume) >> 11) * module->MixVolume) >> 5; } void process_channels(MODULE *module) { int i; for (i = 0; i < 64; i++) { MODULE_CHANNEL *channel = &module->Channel[i]; if (channel->Note < 120) { if (channel->Flag & CHANNEL_RETRIG) { channel->Flag &= ~CHANNEL_RETRIG; if (channel->VChannel) { if (!(module->Flags & FLAG_USEINSTRUMENTS) || (channel->VChannel->NNA = module->Instrument[channel->VChannel->Instrument - 1].NewNoteAction) == NNA_NOTECUT) { if (channel->VChannel->Flag & VCHANNEL_PLAYING) { write_seqtime(); sequence_c(SEQUENCE_STOP_SIGNAL); sequence_c(channel->VChannel - module->VChannel); } } else { if (channel->VChannel->NNA == NNA_NOTEOFF) if ((module->Instrument[channel->VChannel->Instrument - 1].VolumeEnvelope.Flag & (ENVELOPE_ON | ENVELOPE_LOOP_ON)) != ENVELOPE_ON) channel->VChannel->NNA = NNA_NOTEFADE; if (channel->VChannel->NNA == NNA_NOTEFADE) channel->VChannel->Flag |= VCHANNEL_FADING; channel->VChannel->Flag |= VCHANNEL_BACKGROUND; channel->VChannel->channel = NULL; channel->VChannel = NULL; } } if (!channel->VChannel) { int k; for (k = 0; k < 256; k++) { if (!(module->VChannel[k].Flag & VCHANNEL_PLAYING)) { channel->VChannel = &module->VChannel[k]; break; } } if (!channel->VChannel) { for (k = 0; k < 256; k++) { if (module->VChannel[k].Flag & VCHANNEL_BACKGROUND) { write_seqtime(); sequence_c(SEQUENCE_STOP_SIGNAL); sequence_c(k); channel->VChannel = &module->VChannel[k]; break; } } } } if (channel->VChannel) { unsigned char note; channel->VChannel->Flag = VCHANNEL_PLAYING | VCHANNEL_RETRIG; channel->VChannel->channel = channel; channel->VChannel->Note = channel->Note; if (module->Flags & FLAG_USEINSTRUMENTS) note = module->Instrument[(int)channel->Instrument - 1].NoteNote[channel->Note]; else note = channel->Note; channel->pitch = (note - 60) << 8; channel->VChannel->pitch = channel->pitch; channel->VChannel->Instrument = channel->Instrument; channel->VChannel->sample = channel->sample; channel->VChannel->VVolEnv.NextNode = 0; channel->VChannel->VVolEnv.CurTick = 0; channel->VChannel->fadeoutcount = 1024; channel->VChannel->NNA = NNA_NOTECONTINUE; channel->VChannel->Volume = channel->ChannelVolume * channel->Volume; channel->VChannel->Pan = channel->Pan; channel->VChannel->PanSep = channel->PanSep; /* Note: The DUH* fields are set when writing the start command to the DUH sequence. */ //BLEARGH: Anything else to initialise? } } } else if (channel->VChannel) { if (channel->Note == NOTE_CUT) { if (channel->VChannel->Flag & VCHANNEL_PLAYING) { write_seqtime(); sequence_c(SEQUENCE_STOP_SIGNAL); sequence_c(channel->VChannel - module->VChannel); channel->VChannel->Flag &= ~VCHANNEL_PLAYING; channel->VChannel->channel = NULL; channel->VChannel = NULL; } } else { channel->VChannel->Flag |= VCHANNEL_BACKGROUND; if (channel->Note == NOTE_OFF) { if ((module->Instrument[channel->VChannel->Instrument - 1].VolumeEnvelope.Flag & (ENVELOPE_ON | ENVELOPE_LOOP_ON)) != ENVELOPE_ON) channel->VChannel->NNA = NNA_NOTEFADE; else channel->VChannel->NNA = NNA_NOTEOFF; } else channel->VChannel->NNA = NNA_NOTEFADE; if (channel->VChannel->NNA == NNA_NOTEFADE) channel->VChannel->Flag |= VCHANNEL_FADING; /* channel->VChannel->channel = NULL; channel->VChannel = NULL; */ } } } } /* This returns 1 if the envelope finishes. */ int update_envelope(MODULE_VCHANNEL *vchannel, MODULE_ENVELOPE *envelope, MODULE_VENVELOPE *venvelope) { if (!(envelope->Flag & ENVELOPE_ON)) return 0; if (venvelope->NextNode >= envelope->NumNodes) return 1; while (venvelope->CurTick >= envelope->NodeTick[venvelope->NextNode]) { if ((envelope->Flag & ENVELOPE_LOOP_ON) && venvelope->NextNode == envelope->LoopEnd) { venvelope->NextNode = envelope->LoopBegin; venvelope->CurTick = envelope->NodeTick[envelope->LoopBegin]; return 0; } if ((envelope->Flag & ENVELOPE_SUSTAINLOOP) && !(vchannel->Flag & VCHANNEL_BACKGROUND) && venvelope->NextNode == envelope->SustainLoopEnd) { venvelope->NextNode = envelope->SustainLoopBegin; venvelope->CurTick = envelope->NodeTick[envelope->SustainLoopBegin]; return 0; } if (venvelope->NextNode >= envelope->NumNodes) return 1; venvelope->NextNode++; } venvelope->CurTick++; return 0; } /* This assumes the module uses instruments. */ void update_envelopes(MODULE *module, MODULE_VCHANNEL *vchannel) { if (vchannel->Flag & VCHANNEL_PLAYING) { MODULE_ENVELOPE *volenv = &module->Instrument[vchannel->Instrument - 1].VolumeEnvelope; if (update_envelope(vchannel, volenv, &vchannel->VVolEnv)) { if (volenv->NumNodes && volenv->NodeY[volenv->NumNodes - 1] == 0) { write_seqtime(); sequence_c(SEQUENCE_STOP_SIGNAL); sequence_c(vchannel - module->VChannel); vchannel->Flag = 0; if (vchannel->channel) vchannel->channel->VChannel = NULL; vchannel->channel = NULL; } else vchannel->Flag |= VCHANNEL_FADING; } } } /* This assumes the module uses instruments. */ void update_fadeout(MODULE *module, MODULE_VCHANNEL *vchannel) { if (vchannel->Flag & VCHANNEL_PLAYING) { if (vchannel->Flag & VCHANNEL_FADING) { vchannel->fadeoutcount -= module->Instrument[vchannel->Instrument - 1].FadeOut; if (vchannel->fadeoutcount <= 0) { write_seqtime(); sequence_c(SEQUENCE_STOP_SIGNAL); sequence_c(vchannel - module->VChannel); vchannel->Flag = 0; if (vchannel->channel) vchannel->channel->VChannel = NULL; vchannel->channel = NULL; } } } } #define INCLUDE_RETRIG #define INCLUDE_PLAYING void process_vchannels(MODULE *module) { int i; for (i = 0; i < 256; i++) { MODULE_VCHANNEL *vchannel = &module->VChannel[i]; if ((vchannel->Flag & VCHANNEL_PLAYING) && vchannel->channel) { vchannel->Volume = vchannel->channel->ChannelVolume * vchannel->channel->Volume; vchannel->Pan = vchannel->channel->Pan; vchannel->PanSep = vchannel->channel->PanSep; } if (module->Flags & FLAG_USEINSTRUMENTS) { //Update envelopes as required update_envelopes(module, vchannel); //Update fadeout as required update_fadeout(module, vchannel); //Calculate final volume if req //Calculate final pan if req //Process sample vibrato if req } else { //Calculate final volume if required //Calculate final pan if required //Process sample vibrato if required } #ifdef INCLUDE_RETRIG if (vchannel->Flag & VCHANNEL_RETRIG) { vchannel->Flag &= ~VCHANNEL_RETRIG; if (vchannel->sample) { int C5Speed = vchannel->sample->C5Speed; int adjust; #ifdef STEREO_SAMPLES_COUNT_AS_TWO if (vchannel->sample->Flag & SAMPLE_STEREO) C5Speed >>= 1; #endif write_seqtime(); sequence_c(SEQUENCE_START_SIGNAL); sequence_c(vchannel - module->VChannel); sequence_cl(sample_signal[vchannel->sample - module->Sample]); sequence_cl(0); // sample position: 65536 is one second vchannel->DUHvol = calculate_volume(module, vchannel); sequence_w(vchannel->DUHvol - (vchannel->DUHvol >> 16)); adjust = floor(12 * 256 * (log(C5Speed / 65536.0) / log(2.0)) + 0.5); /* pitch */ sequence_w(adjust + vchannel->pitch); vchannel->DUHpan = 32; } else vchannel->Flag = 0; } #endif #ifdef INCLUDE_PLAYING if (vchannel->Flag & VCHANNEL_PLAYING) { //Update to vchannel->channel->stuff if different from vchannel->stuff. //stuff would be volume, panning, pitch, etc. { unsigned int volume = calculate_volume(module, vchannel); if (volume != vchannel->DUHvol) { vchannel->DUHvol = volume; write_seqtime(); sequence_c(SEQUENCE_SET_VOLUME); sequence_c(vchannel - module->VChannel); sequence_w(volume - (volume >> 16)); } } { int pan = vchannel->Pan + vchannel->PanSep; if (pan < 0) pan = 0; else if (pan > 64) pan = 64; if (pan != vchannel->DUHpan) { vchannel->DUHpan = pan; write_seqtime(); sequence_c(SEQUENCE_SET_PARAMETER); sequence_c(vchannel - module->VChannel); sequence_c(SPANPARAM_PAN); sequence_l(((int)pan << 3) - 256); } } if (vchannel->channel) { if (vchannel->channel->pitch != vchannel->pitch) { int C5Speed = vchannel->sample->C5Speed; int adjust; #ifdef STEREO_SAMPLES_COUNT_AS_TWO if (vchannel->sample->Flag & SAMPLE_STEREO) C5Speed >>= 1; #endif vchannel->pitch = vchannel->channel->pitch; write_seqtime(); sequence_c(SEQUENCE_SET_PITCH); sequence_c(vchannel - module->VChannel); adjust = floor(12 * 256 * (log(C5Speed / 65536.0) / log(2.0)) + 0.5); sequence_w(adjust + vchannel->pitch); } } if (vchannel->Flag & VCHANNEL_BACKGROUND) { //Process vchannel->NNA //If the note has ended then: /* { write_seqtime(); sequence_c(SEQUENCE_STOP_SIGNAL); sequence_c(vchannel - module->VChannel); vchannel->Flag = 0; } */ } else { //Process note } } #endif } } void generate_patterns(MODULE *module) { int patnum = module->Order[0]; int row; MODULE_PATTERN *pattern; MODULE_NOTE *note, *note_end; module->processorder = 0; module->processrow = 0; module->breakrow = 0; module->rowcount = 1; module->tick = module->Speed; goto start_the_loop; /* This ungainly goto will be averted later. */ for (;;) { // Set note vol/freq to vol/freq set for each channel module->tick--; if (module->tick == 0) { module->tick = module->Speed; module->rowcount--; if (module->rowcount == 0) { module->rowcount = 1; module->processrow++; if (module->processrow >= pattern->NumRows) { module->processrow = module->breakrow; module->breakrow = 0; module->processorder++; start_the_loop: for (; ; module->processorder++) { if (module->processorder >= module->NumOrders) return; patnum = module->Order[module->processorder]; if (patnum < module->NumPatterns) break; #ifdef INVALID_ORDERS_END_SONG if (patnum != ORDER_SKIP) return; #else if (patnum == ORDER_END) return; #endif } printf(" Order %i, Pattern %i\n", module->processorder, patnum); pattern = &module->Pattern[patnum]; note = pattern->Note; note_end = note + pattern->NumNotes; } row = module->processrow; { MODULE_NOTE *note_start = note; while (note < note_end && note->Channel >= 0) update_pattern_variables(module, note++); note = note_start; } reset_effects(module); //Should this code go before updating the pattern variables? while (note < note_end) { if (note->Channel < 0) { note++; break; } process_note_data(module, note++); } } else update_effects(module); } else update_effects(module); process_channels(module); /* Output sound!!! */ process_vchannels(module); /* Increment time by one tick. */ { int inc = ((65536.0 * 60.0) / (4.0 * 6.0)) / module->Tempo; seqtime += inc; songtime += inc; } } } void stop_notes(MODULE *module) { int c; for (c = 0; c < 256; c++) { if (module->VChannel[c].Flag & VCHANNEL_PLAYING) { write_seqtime(); sequence_c(SEQUENCE_STOP_SIGNAL); sequence_c(c); } } } void generate_sequence(MODULE *module) { int i; for (i = 0; i < 64; i++) { module->Channel[i].Note = 255; module->Channel[i].Instrument = 0; module->Channel[i].sample = NULL; module->Channel[i].VChannel = NULL; module->Channel[i].lastvolslide = 0; module->Channel[i].lastDKL = 0; module->Channel[i].lastN = 0; module->Channel[i].lastW = 0; module->Channel[i].lastEF = 0; module->Channel[i].lastG = 0; module->Channel[i].lastT = 0; } for (i = 0; i < 256; i++) module->VChannel[i].Flag = 0; songtime = 0; generate_patterns(module); /* for (i = 0; i < module->NumOrders; i++) { if (module->Order[i] < module->NumPatterns) write_pattern(module, &module->Pattern[module->Order[i]]); else { #ifdef INVALID_ORDERS_END_SONG if (module->Order[i] != ORDER_SKIP) break; #else if (module->Order[i] == ORDER_END) break; #endif } } */ stop_notes(module); /* Negative time to indicate the end of the sequence. */ sequence_cl(-1); if (errno) return; } void write_sequence(PACKFILE *f) { pack_mputl(SIGTYPE_SEQUENCE, f); pack_iputl(seqpos, f); pack_fwrite(seqdata, seqpos, f); } void free_sequence() { free(seqdata); seqdata = NULL; seqpos = 0; max_seqdata = 0; seqtime = 0; } int save_it_to_duh(MODULE *module, const char *filename) { int i; int n_samples = 0; PACKFILE *f = pack_fopen(filename, F_WRITE_PACKED); if (!f) return 1; /* Write signature. */ pack_mputl(DUH_SIGNATURE, f); /* Create array of true samples. */ sample_signal = malloc(module->NumSamples * sizeof(*sample_signal)); for (i = 0; i < module->NumSamples; i++) { if (sample_is_valid(&module->Sample[i])) { sample_signal[i] = first_sample + n_samples; if (module->Sample[i].Flag & SAMPLE_STEREO) n_samples += 3; /* COMB,SAMP,SAMP */ else n_samples += 2; /* SPAN,SAMP */ } else sample_signal[i] = -1; } printf("Generating sequence\n"); generate_sequence(module); /* Write length of song. */ pack_iputl(songtime, f); /* Write number of signals - one for the sequence, one for each sample. */ pack_iputl(1 + n_samples, f); if (errno) {pack_fclose(f); free(sample_signal); return 1;} printf("Writing sequence\n"); write_sequence(f); free_sequence(); if (errno) {pack_fclose(f); free(sample_signal); return 1;} printf("Writing samples"); n_samples = 0; for (i = 0; i < module->NumSamples; i++) { if ((i & 7) == 0) printf("\n "); printf("%4d", i + 1); if (sample_is_valid(&module->Sample[i])) { if (module->Sample[i].Flag & SAMPLE_STEREO) { write_stereo_sample(&module->Sample[i], n_samples, f); n_samples += 3; } else { write_mono_sample(&module->Sample[i], n_samples, f); n_samples += 2; } if (errno) {pack_fclose(f); free(sample_signal); return 1;} } } printf("\n"); free(sample_signal); sample_signal = NULL; pack_fclose(f); if (errno) {pack_fclose(f); return 1;} return 0; } #undef first_sample void usage() { allegro_message( "Usage: cit.exe filename.it [filename.duh]\n" "Converts an Impulse Tracker file into a Dynamic Universal Harmony.\n" ); exit(1); } int main(int argc, char *argv[]) { MODULE *module; int rv; char *outmod; if (argc < 2 || argc > 3) usage(); allegro_init(); /* Make the output file name if it wasn't specified */ if (argc == 2) { int size = sizeof(char) * (ustrsizez(argv[1]) + 32); outmod = malloc(size); if (!outmod) { allegro_message("Ran out of memory while trying to allocate %i bytes. (%s:(%i))\n", size, __FILE__, __LINE__); return -1; } ustrzcpy(outmod, size, argv[1]); replace_extension(outmod, argv[1], "duh", size); } else outmod = argv[2]; module = load_it(argv[1]); if (!module) { allegro_exit(); allegro_message("Unable to load %s!\n", argv[1]); return 1; } rv = save_it_to_duh(module, outmod); destroy_it(module); if (rv) { delete_file(outmod); allegro_exit(); allegro_message("Unable to save %s!\n", outmod); return 1; } if (argc == 2 && outmod) free(outmod); return 0; } END_OF_MAIN();