diff --git a/dumb/CMakeLists.txt b/dumb/CMakeLists.txt index c35dc7da09..591e314002 100644 --- a/dumb/CMakeLists.txt +++ b/dumb/CMakeLists.txt @@ -61,6 +61,8 @@ add_library( dumb src/it/loadmod2.c src/it/loadmtm.c src/it/loadmtm2.c + src/it/loadokt.c + src/it/loadokt2.c src/it/loadoldpsm.c src/it/loadoldpsm2.c src/it/loadpsm.c @@ -84,6 +86,8 @@ add_library( dumb src/it/readmod.c src/it/readmod2.c src/it/readmtm.c + src/it/readokt.c + src/it/readokt2.c src/it/readoldpsm.c src/it/readpsm.c src/it/readptm.c diff --git a/dumb/include/dumb.h b/dumb/include/dumb.h index 0b42b4d6dd..d82afe6f69 100644 --- a/dumb/include/dumb.h +++ b/dumb/include/dumb.h @@ -408,6 +408,7 @@ DUH *DUMBEXPORT dumb_load_old_psm(const char * filename); DUH *DUMBEXPORT dumb_load_mtm(const char *filename); DUH *DUMBEXPORT dumb_load_riff(const char *filename); DUH *DUMBEXPORT dumb_load_asy(const char *filename); +DUH *DUMBEXPORT dumb_load_okt(const char *filename); DUH *DUMBEXPORT dumb_read_it(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_xm(DUMBFILE *f); @@ -421,6 +422,7 @@ DUH *DUMBEXPORT dumb_read_old_psm(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_mtm(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_riff(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_asy(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_okt(DUMBFILE *f); DUH *DUMBEXPORT dumb_load_it_quick(const char *filename); DUH *DUMBEXPORT dumb_load_xm_quick(const char *filename); @@ -434,6 +436,7 @@ DUH *DUMBEXPORT dumb_load_old_psm_quick(const char * filename); DUH *DUMBEXPORT dumb_load_mtm_quick(const char *filename); DUH *DUMBEXPORT dumb_load_riff_quick(const char *filename); DUH *DUMBEXPORT dumb_load_asy_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_okt_quick(const char *filename); DUH *DUMBEXPORT dumb_read_it_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_xm_quick(DUMBFILE *f); @@ -447,6 +450,7 @@ DUH *DUMBEXPORT dumb_read_old_psm_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_mtm_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_riff_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_asy_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_okt_quick(DUMBFILE *f); int32 DUMBEXPORT dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder); void DUMBEXPORT dumb_it_do_initial_runthrough(DUH *duh); diff --git a/dumb/include/internal/it.h b/dumb/include/internal/it.h index 36b083e0c4..0c8bf144ce 100644 --- a/dumb/include/internal/it.h +++ b/dumb/include/internal/it.h @@ -300,7 +300,18 @@ struct IT_SAMPLE #define IT_PTM_NOTE_SLIDE_DOWN_RETRIG 36 #define IT_PTM_NOTE_SLIDE_UP_RETRIG 37 -#define IT_N_EFFECTS 38 +/* More effects needed for OKT compatibility */ +#define IT_OKT_NOTE_SLIDE_DOWN 38 +#define IT_OKT_NOTE_SLIDE_DOWN_ROW 39 +#define IT_OKT_NOTE_SLIDE_UP 40 +#define IT_OKT_NOTE_SLIDE_UP_ROW 41 +#define IT_OKT_ARPEGGIO_3 42 +#define IT_OKT_ARPEGGIO_4 43 +#define IT_OKT_ARPEGGIO_5 44 +#define IT_OKT_VOLUME_SLIDE_DOWN 45 +#define IT_OKT_VOLUME_SLIDE_UP 46 + +#define IT_N_EFFECTS 47 /* These represent the top nibble of the command value. */ #define IT_S_SET_FILTER 0 /* Greyed out in IT... */ @@ -399,6 +410,8 @@ struct IT_PATTERN #define IT_WAS_A_669 1024 +#define IT_WAS_AN_OKT 2048 + #define IT_ORDER_END 255 #define IT_ORDER_SKIP 254 @@ -586,7 +599,8 @@ struct IT_CHANNEL unsigned char new_note_action; - int arpeggio; + unsigned int arpeggio; + int arpeggio_shift; unsigned char retrig; unsigned char xm_retrig; int retrig_tick; @@ -601,7 +615,7 @@ struct IT_CHANNEL int portamento; int toneporta; int toneslide; - unsigned char toneslide_tick, last_toneslide_tick, ptm_toneslide, ptm_last_toneslide; + unsigned char toneslide_tick, last_toneslide_tick, ptm_toneslide, ptm_last_toneslide, okt_toneslide; unsigned char destnote; unsigned char toneslide_retrig; diff --git a/dumb/src/it/itrender.c b/dumb/src/it/itrender.c index f1ff9721d5..34a698d7f3 100644 --- a/dumb/src/it/itrender.c +++ b/dumb/src/it/itrender.c @@ -218,6 +218,7 @@ static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src) dst->new_note_action = src->new_note_action; dst->arpeggio = src->arpeggio; + dst->arpeggio_shift = src->arpeggio_shift; dst->retrig = src->retrig; dst->xm_retrig = src->xm_retrig; dst->retrig_tick = src->retrig_tick; @@ -235,6 +236,7 @@ static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src) dst->last_toneslide_tick = src->last_toneslide_tick; dst->ptm_toneslide = src->ptm_toneslide; dst->ptm_last_toneslide = src->ptm_last_toneslide; + dst->okt_toneslide = src->okt_toneslide; dst->destnote = src->destnote; dst->glissando = src->glissando; @@ -807,6 +809,7 @@ static void reset_channel_effects(IT_CHANNEL *channel) channel->panslide = 0; channel->channelvolslide = 0; channel->arpeggio = 0; + channel->arpeggio_shift = 0; channel->retrig = 0; if (channel->xm_retrig) { channel->xm_retrig = 0; @@ -822,6 +825,7 @@ static void reset_channel_effects(IT_CHANNEL *channel) channel->ptm_last_toneslide = 0; channel->ptm_toneslide = 0; channel->toneslide_tick = 0; + channel->okt_toneslide = 0; if (channel->playing) { channel->playing->vibrato_n = 0; channel->playing->tremolo_speed = 0; @@ -1166,8 +1170,7 @@ static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer) update_tremor(channel); - channel->arpeggio = (channel->arpeggio << 4) | (channel->arpeggio >> 8); - channel->arpeggio &= 0xFFF; + if (channel->arpeggio_shift) channel->arpeggio = (channel->arpeggio << 8) | (channel->arpeggio >> channel->arpeggio_shift); update_retrig(sigrenderer, channel); @@ -1176,7 +1179,15 @@ static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer) if (playing) { playing->slide += channel->portamento; - if (channel->ptm_toneslide) { + if (channel->okt_toneslide) { + if (channel->okt_toneslide--) { + playing->note += channel->toneslide; + if (playing->note >= 120) { + if (channel->toneslide < 0) playing->note = 0; + else playing->note = 119; + } + } + } else if (channel->ptm_toneslide) { if (--channel->toneslide_tick == 0) { channel->toneslide_tick = channel->ptm_toneslide; playing->note += channel->toneslide; @@ -2213,7 +2224,8 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than v = channel->lastJ; channel->lastJ = v; } - channel->arpeggio = v; + channel->arpeggio = ((v & 0xF0) << 4) | (v & 0x0F); + channel->arpeggio_shift = 16; } break; case IT_SET_CHANNEL_VOLUME: @@ -2787,6 +2799,63 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than channel->toneslide_tick += channel->ptm_toneslide; } break; + + case IT_OKT_NOTE_SLIDE_DOWN: + case IT_OKT_NOTE_SLIDE_DOWN_ROW: + channel->toneslide = -entry->effectvalue; + channel->okt_toneslide = (entry->effect == IT_OKT_NOTE_SLIDE_DOWN) ? 255 : 1; + break; + + case IT_OKT_NOTE_SLIDE_UP: + case IT_OKT_NOTE_SLIDE_UP_ROW: + channel->toneslide = entry->effectvalue; + channel->okt_toneslide = (entry->effect == IT_OKT_NOTE_SLIDE_UP) ? 255 : 1; + break; + + case IT_OKT_ARPEGGIO_3: + case IT_OKT_ARPEGGIO_4: + case IT_OKT_ARPEGGIO_5: + { + unsigned char low = -(entry->effectvalue >> 4); + unsigned char high = entry->effectvalue & 0x0F; + + switch (entry->effect) + { + case IT_OKT_ARPEGGIO_3: + channel->arpeggio = (low << 16) | high; + channel->arpeggio_shift = 16; + break; + + case IT_OKT_ARPEGGIO_4: + channel->arpeggio = (high << 16) | low; + channel->arpeggio_shift = 24; + break; + + case IT_OKT_ARPEGGIO_5: + channel->arpeggio = (high << 16) | (high << 8); + channel->arpeggio_shift = 16; + break; + } + } + break; + + case IT_OKT_VOLUME_SLIDE_DOWN: + if ( entry->effectvalue <= 16 ) channel->volslide = -entry->effectvalue; + else + { + channel->volume -= entry->effectvalue - 16; + if (channel->volume > 64) channel->volume = 0; + } + break; + + case IT_OKT_VOLUME_SLIDE_UP: + if ( entry->effectvalue <= 16 ) channel->volslide = entry->effectvalue; + else + { + channel->volume += entry->effectvalue - 16; + if (channel->volume > 64) channel->volume = 64; + } + break; } } @@ -3806,7 +3875,7 @@ static void process_all_playing(DUMB_IT_SIGRENDERER *sigrenderer) if ( channel->arpeggio > 0xFF ) playing->delta = playing->sample->C5_speed * (1.f / 65536.f); } - else*/ playing->delta *= (float)pow(DUMB_SEMITONE_BASE, channel->arpeggio >> 8);/* + else*/ playing->delta *= (float)pow(DUMB_SEMITONE_BASE, (signed char)(channel->arpeggio >> channel->arpeggio_shift));/* }*/ playing->filter_cutoff = channel->filter_cutoff; @@ -3941,6 +4010,14 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) sigrenderer->processorder = -1; continue; } + if (sigdata->flags & IT_WAS_AN_OKT) { + /* Reset some things */ + sigrenderer->speed = sigdata->speed; + sigrenderer->tempo = sigdata->tempo; + for (n = 0; n < DUMB_IT_N_CHANNELS; n++) { + xm_note_off(sigdata, &sigrenderer->channel[n]); + } + } } n = sigdata->order[sigrenderer->processorder]; @@ -4038,7 +4115,9 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) return 1; } - if (!(sigdata->flags & IT_OLD_EFFECTS)) + if (sigdata->flags & IT_WAS_AN_OKT) + update_effects(sigrenderer); + else if (!(sigdata->flags & IT_OLD_EFFECTS)) update_smooth_effects(sigrenderer); } else { { @@ -4915,6 +4994,7 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha channel->toneslide = 0; channel->ptm_toneslide = 0; channel->ptm_last_toneslide = 0; + channel->okt_toneslide = 0; channel->midi_state = 0; channel->lastvolslide = 0; channel->lastDKL = 0; diff --git a/dumb/src/it/loadokt.c b/dumb/src/it/loadokt.c new file mode 100644 index 0000000000..2139d49e80 --- /dev/null +++ b/dumb/src/it/loadokt.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadokt.c - Code to read an Oktalyzer module / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_okt_quick(): loads an OKT file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_okt_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_okt_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/dumb/src/it/loadokt2.c b/dumb/src/it/loadokt2.c new file mode 100644 index 0000000000..aa752c5824 --- /dev/null +++ b/dumb/src/it/loadokt2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadokt2.c - Function to read an Oktalyzer / / \ \ + * module file, opening and closing | < / \_ + * it for you, and do an initial run- | \/ /\ / + * through. \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_load_okt(const char *filename) +{ + DUH *duh = dumb_load_okt_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/dumb/src/it/readasy.c b/dumb/src/it/readasy.c index 3cc89ab172..5738b78a1b 100644 --- a/dumb/src/it/readasy.c +++ b/dumb/src/it/readasy.c @@ -124,8 +124,7 @@ assumed not to be an instrument name, and is probably a message. sample->flags = IT_SAMPLE_EXISTS; sample->default_pan = 0; - sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 * pow( DUMB_SEMITONE_BASE, key_offset ) );//( long )( 16726.0 * pow( DUMB_PITCH_BASE, -finetune * 32 ) ); + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 * pow( DUMB_SEMITONE_BASE, key_offset ) );//( long )( 16726.0 * pow( DUMB_PITCH_BASE, finetune * 32 ) ); sample->finetune = finetune * 32; // the above line might be wrong diff --git a/dumb/src/it/readokt.c b/dumb/src/it/readokt.c new file mode 100644 index 0000000000..cab3c7a05f --- /dev/null +++ b/dumb/src/it/readokt.c @@ -0,0 +1,542 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readokt.c - Code to read an Oktalyzer module / / \ \ + * from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_okt_read_pattern(IT_PATTERN *pattern, const unsigned char *data, int length, int n_channels) +{ + int pos; + int channel; + int row; + int n_rows; + IT_ENTRY *entry; + + if (length < 2) return -1; + + n_rows = (data[0] << 8) | data[1]; + if (!n_rows) n_rows = 64; + + if (length < 2 + (n_rows * n_channels * 4)) return -1; + + pattern->n_rows = n_rows; + + /* compute number of entries */ + pattern->n_entries = n_rows; /* Account for the row end markers */ + pos = 2; + for (row = 0; row < pattern->n_rows; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (data[pos+0] | data[pos+2]) + pattern->n_entries++; + pos += 4; + } + } + + pattern->entry = (IT_ENTRY *) malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) + return -1; + + entry = pattern->entry; + pos = 2; + for (row = 0; row < n_rows; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (data[pos+0] | data[pos+2]) { + entry->channel = channel; + entry->mask = 0; + + if (data[pos+0] > 0 && data[pos+0] <= 36) { + entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT; + + entry->note = data[pos+0] + 35; + entry->instrument = data[pos+1] + 1; + } + + entry->effect = 0; + entry->effectvalue = data[pos+3]; + + switch (data[pos+2]) { + case 2: if (data[pos+3]) entry->effect = IT_PORTAMENTO_DOWN; break; // XXX code calls this rs_portu, but it's adding to the period, which decreases the pitch + case 13: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN; break; + case 21: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN_ROW; break; + + case 1: if (data[pos+3]) entry->effect = IT_PORTAMENTO_UP; break; // XXX same deal here, increasing the pitch + case 17: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP; break; + case 30: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP_ROW; break; + + case 10: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_3; break; + case 11: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_4; break; + case 12: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_5; break; + + case 15: entry->effect = IT_S; entry->effectvalue = EFFECT_VALUE(IT_S_SET_FILTER, data[pos+3] & 0x0F); break; + + case 25: entry->effect = IT_JUMP_TO_ORDER; break; + + case 27: entry->note = IT_NOTE_OFF; entry->mask |= IT_ENTRY_NOTE; break; + + case 28: entry->effect = IT_SET_SPEED; break; + + case 31: + if ( data[pos+3] <= 0x40 ) entry->effect = IT_SET_CHANNEL_VOLUME; + else if ( data[pos+3] <= 0x50 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x40; } + else if ( data[pos+3] <= 0x60 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x50; } + else if ( data[pos+3] <= 0x70 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x50; } + else if ( data[pos+3] <= 0x80 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x60; } + break; + } + + if ( entry->effect ) entry->mask |= IT_ENTRY_EFFECT; + + entry++; + } + pos += 4; + } + IT_SET_END_ROW(entry); + entry++; + } + + return 0; +} + + + +static void it_okt_read_sample_header(IT_SAMPLE *sample, const unsigned char * data) +{ + int loop_start, loop_length; + + memcpy(sample->name, data, 20); + sample->name[20] = 0; + + sample->filename[0] = 0; + + sample->length = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23]; + sample->global_volume = 64; + sample->default_volume = data[29]; + loop_start = ((data[24] << 8) | data[25]) << 1; + loop_length = ((data[26] << 8) | data[27]) << 1; + sample->sus_loop_start = loop_start; + sample->sus_loop_end = loop_start + loop_length; + + if (sample->length <= 0) { + sample->flags = 0; + return; + } + + sample->flags = IT_SAMPLE_EXISTS; + + sample->default_pan = 0; + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 ); //(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32)); + sample->finetune = 0; + + if (sample->sus_loop_end > sample->length) + sample->sus_loop_end = sample->length; + + if (loop_length > 2) + sample->flags |= IT_SAMPLE_SUS_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; +} + + + +static int it_okt_read_sample_data(IT_SAMPLE *sample, const char * data, int length) +{ + if (length && sample->length) { + if (length < sample->length) { + sample->length = length; + if (length < sample->sus_loop_end) sample->sus_loop_end = length; + } + + sample->data = malloc(length); + + if (!sample->data) + return -1; + + memcpy(sample->data, data, length); + } + + return 0; +} + + + +typedef struct IFF_CHUNK IFF_CHUNK; +typedef struct IFF_CHUNKED IFF_CHUNKED; + +struct IFF_CHUNK +{ + unsigned type; + unsigned char * data; + unsigned size; +}; + +struct IFF_CHUNKED +{ + unsigned chunk_count; + IFF_CHUNK * chunks; +}; + + + +static IFF_CHUNKED *dumbfile_read_okt(DUMBFILE *f) +{ + IFF_CHUNKED *mod = (IFF_CHUNKED *) malloc(sizeof(*mod)); + if (!mod) return NULL; + + mod->chunk_count = 0; + mod->chunks = 0; + + for (;;) + { + long bytes_read; + IFF_CHUNK * chunk = ( IFF_CHUNK * ) realloc( mod->chunks, ( mod->chunk_count + 1 ) * sizeof( IFF_CHUNK ) ); + if ( !chunk ) + { + if ( mod->chunks ) free( mod->chunks ); + free( mod ); + return NULL; + } + mod->chunks = chunk; + chunk += mod->chunk_count; + + bytes_read = dumbfile_mgetl( f ); + if ( bytes_read < 0 ) break; + + chunk->type = bytes_read; + chunk->size = dumbfile_mgetl( f ); + + chunk->data = (unsigned char *) malloc( chunk->size ); + if ( !chunk->data ) + { + free( mod->chunks ); + free( mod ); + return NULL; + } + + bytes_read = dumbfile_getnc( ( char * ) chunk->data, chunk->size, f ); + if ( bytes_read < (long)chunk->size ) + { + free( mod->chunks ); + free( mod ); + return NULL; + } + + mod->chunk_count++; + } + + return mod; +} + +void free_okt(IFF_CHUNKED * mod) +{ + unsigned i; + if (mod) + { + if (mod->chunks) + { + for (i = 0; i < mod->chunk_count; i++) + { + if (mod->chunks[i].data) free(mod->chunks[i].data); + } + free(mod->chunks); + } + free(mod); + } +} + +const IFF_CHUNK * get_chunk_by_type(IFF_CHUNKED * mod, unsigned type, unsigned offset) +{ + unsigned i; + if (mod) + { + if (mod->chunks) + { + for (i = 0; i < mod->chunk_count; i++) + { + if (mod->chunks[i].type == type) + { + if (!offset) return &mod->chunks[i]; + else offset--; + } + } + } + } + return NULL; +} + +unsigned get_chunk_count(IFF_CHUNKED *mod, unsigned type) +{ + unsigned i, count = 0; + if (mod) + { + if (mod->chunks) + { + for (i = 0; i < mod->chunk_count; i++) + { + if (mod->chunks[i].type == type) count++; + } + } + } + return count; +} + + +static DUMB_IT_SIGDATA *it_okt_load_sigdata(DUMBFILE *f, int restrict) +{ + DUMB_IT_SIGDATA *sigdata; + unsigned n_channels; + unsigned i, j, k, l; + IFF_CHUNKED *mod; + const IFF_CHUNK *chunk; + + char signature[8]; + + if (dumbfile_getnc(signature, 8, f) < 8 || + memcmp(signature, "OKTASONG", 8)) { + return NULL; + } + + mod = dumbfile_read_okt(f); + if (!mod) + return NULL; + + sigdata = (DUMB_IT_SIGDATA *) malloc(sizeof(*sigdata)); + if (!sigdata) { + free_okt(mod); + return NULL; + } + + sigdata->name[0] = 0; + + chunk = get_chunk_by_type(mod, DUMB_ID('S','P','E','E'), 0); + if (!chunk || chunk->size < 2) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->speed = (chunk->data[0] << 8) | chunk->data[1]; + + chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0); + if (!chunk || chunk->size < 32) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_samples = chunk->size / 32; + + chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0); + if (!chunk || chunk->size < 8) { + free(sigdata); + free_okt(mod); + return NULL; + } + + n_channels = 0; + + for (i = 0; i < 4; i++) { + j = (chunk->data[i * 2] << 8) | chunk->data[i * 2 + 1]; + if (!j) n_channels++; + else if (j == 1) n_channels += 2; + } + + if (!n_channels) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_pchannels = n_channels; + + sigdata->sample = (IT_SAMPLE *) malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + for (i = 0; i < (unsigned)sigdata->n_samples; i++) + sigdata->sample[i].data = NULL; + + chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0); + + for (i = 0; i < (unsigned)sigdata->n_samples; i++) { + it_okt_read_sample_header(&sigdata->sample[i], chunk->data + 32 * i); + } + + sigdata->restart_position = 0; + + chunk = get_chunk_by_type(mod, DUMB_ID('P','L','E','N'), 0); + if (!chunk || chunk->size < 2) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_orders = (chunk->data[0] << 8) | chunk->data[1]; + // what if this is > 128? + + if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + chunk = get_chunk_by_type(mod, DUMB_ID('P','A','T','T'), 0); + if (!chunk || chunk->size < (unsigned)sigdata->n_orders) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->order = (unsigned char *) malloc(sigdata->n_orders); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + memcpy(sigdata->order, chunk->data, sigdata->n_orders); + + /* Work out how many patterns there are. */ + chunk = get_chunk_by_type(mod, DUMB_ID('S','L','E','N'), 0); + if (!chunk || chunk->size < 2) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_patterns = (chunk->data[0] << 8) | chunk->data[1]; + + j = get_chunk_count(mod, DUMB_ID('P','B','O','D')); + if (sigdata->n_patterns > (int)j) sigdata->n_patterns = (int)j; + + if (!sigdata->n_patterns) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->pattern = (IT_PATTERN *) malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + for (i = 0; i < (unsigned)sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + /* Read in the patterns */ + for (i = 0; i < (unsigned)sigdata->n_patterns; i++) { + chunk = get_chunk_by_type(mod, DUMB_ID('P','B','O','D'), i); + if (it_okt_read_pattern(&sigdata->pattern[i], chunk->data, chunk->size, n_channels) != 0) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + } + + /* And finally, the sample data */ + k = get_chunk_count(mod, DUMB_ID('S','B','O','D')); + for (i = 0, j = 0; i < (unsigned)sigdata->n_samples, j < k; i++) { + if (sigdata->sample[i].flags & IT_SAMPLE_EXISTS) { + chunk = get_chunk_by_type(mod, DUMB_ID('S','B','O','D'), j); + if (it_okt_read_sample_data(&sigdata->sample[i], (const char *)chunk->data, chunk->size)) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + j++; + } + } + + chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0); + + for (i = 0, j = 0; i < n_channels, j < 4; j++) { + k = (chunk->data[j * 2] << 8) | chunk->data[j * 2 + 1]; + l = (j == 1 || j == 2) ? 48 : 16; + if (k == 0) { + sigdata->channel_pan[i++] = l; + } + else if (k == 1) { + sigdata->channel_pan[i++] = l; + sigdata->channel_pan[i++] = l; + } + } + + free_okt(mod); + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_WAS_AN_OKT | IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + /* We want 50 ticks per second; 50/6 row advances per second; + * 50*10=500 row advances per minute; 500/4=125 beats per minute. + */ + sigdata->tempo = 125; + sigdata->pan_separation = 128; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + memset(sigdata->channel_pan + n_channels, 32, DUMB_IT_N_CHANNELS - n_channels); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *DUMBEXPORT dumb_read_okt_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_okt_load_sigdata(f, 0); + + if (!sigdata) + return NULL; + + { + const char *tag[1][2]; + tag[0][0] = "FORMAT"; + tag[0][1] = "Oktalyzer"; + return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/dumb/src/it/readokt2.c b/dumb/src/it/readokt2.c new file mode 100644 index 0000000000..66dd1f6d4a --- /dev/null +++ b/dumb/src/it/readokt2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readokt2.c - Function to read an Oktalyzer / / \ \ + * module from an open file and do | < / \_ + * an initial run-through. | \/ /\ / + * \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_okt(DUMBFILE *f) +{ + DUH *duh = dumb_read_okt_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/dumb/vc6/dumb_static/dumb_static.vcproj b/dumb/vc6/dumb_static/dumb_static.vcproj index 896ce8a8fa..3f1373cc31 100644 --- a/dumb/vc6/dumb_static/dumb_static.vcproj +++ b/dumb/vc6/dumb_static/dumb_static.vcproj @@ -1564,6 +1564,14 @@ RelativePath="..\..\src\it\loadmtm2.c" > + + + + @@ -1782,6 +1790,14 @@ RelativePath="..\..\src\it\readmtm.c" > + + + +