Example #1
0
int fmt_aiff_export_body(disko_t *fp, const uint8_t *data, size_t length)
{
        struct aiff_writedata *awd = fp->userdata;

        if (length % awd->bps) {
                log_appendf(4, "AIFF export: received uneven length");
                return DW_ERROR;
        }

        awd->numbytes += length;

        if (awd->swap) {
                const int16_t *ptr = (const int16_t *) data;
                uint16_t v;

                length /= 2;
                while (length--) {
                        v = *ptr;
                        v = bswapBE16(v);
                        disko_write(fp, &v, 2);
                        ptr++;
                }
        } else {
                disko_write(fp, data, length);
        }

        return DW_OK;
}
Example #2
0
static int _read_iff(dmoz_file_t *file, song_sample_t *smp, const uint8_t *data, size_t length)
{
        chunk_t chunk;
        size_t pos = 0;
        chunk_t vhdr, body, name, comm, auth, anno, ssnd; // butt

        if (!iff_chunk_read(&chunk, data, length, &pos))
                return 0;
        if (chunk.id != ID_FORM)
                return 0;

        // jump "into" the FORM chunk
        // if (pos < length), there's more data after the FORM chunk -- but I don't care about this scenario
        pos = 0;
        length = MIN(length, chunk.size);
        data = chunk.data->FORM.data;

        /* the header is already byteswapped, but anything in 'chunk' will need to be swapped as needed
        because the structure is a const pointing into the data itself */
        switch (bswapBE32(chunk.data->FORM.filetype)) {
        case ID_8SVX:
                // shut up, gcc
                ZEROIZE(vhdr);
                ZEROIZE(body);
                ZEROIZE(name);
                ZEROIZE(auth);
                ZEROIZE(anno);

                while (iff_chunk_read(&chunk, data, length, &pos)) {
                        switch (chunk.id) {
                                case ID_VHDR: vhdr = chunk; break;
                                case ID_BODY: body = chunk; break;
                                case ID_NAME: name = chunk; break;
                                case ID_AUTH: auth = chunk; break;
                                case ID_ANNO: anno = chunk; break;
                                default: break;
                        }
                }
                if (!(vhdr.id && body.id))
                        return 0;

                if (vhdr.data->VHDR.compression) {
                        log_appendf(4, "error: compressed 8SVX files are unsupported");
                        return 0;
                }
                if (vhdr.data->VHDR.num_octaves != 1) {
                        log_appendf(4, "warning: 8SVX file contains %d octaves",
                                vhdr.data->VHDR.num_octaves);
                }

                if (file) {
                        file->description = "8SVX sample";
                        file->type = TYPE_SAMPLE_PLAIN;
                }
                if (!name.id) name = auth;
                if (!name.id) name = anno;
                if (name.id) {
                        if (file) {
                                file->title = calloc(1, name.size + 1);
                                memcpy(file->title, name.data->bytes, name.size);
                                file->title[name.size] = '\0';
                        }
                        if (smp) {
                                int len = MIN(25, name.size);
                                memcpy(smp->name, name.data->bytes, len);
                                smp->name[len] = 0;
                        }
                }

                if (smp) {
                        smp->c5speed = bswapBE16(vhdr.data->VHDR.smp_per_sec);
                        smp->length = body.size;

                        csf_read_sample(smp, SF_BE | SF_PCMS | SF_8 | SF_M, body.data->bytes, body.size);

                        smp->volume = 64*4;
                        smp->global_volume = 64;

                        // this is done kinda weird
                        smp->loop_end = bswapBE32(vhdr.data->VHDR.smp_highoct_repeat);
                        if (smp->loop_end) {
                                smp->loop_start = bswapBE32(vhdr.data->VHDR.smp_highoct_1shot);
                                smp->loop_end += smp->loop_start;
                                if (smp->loop_start > smp->length)
                                        smp->loop_start = 0;
                                if (smp->loop_end > smp->length)
                                        smp->loop_end = smp->length;
                                if (smp->loop_start + 2 < smp->loop_end)
                                        smp->flags |= CHN_LOOP;
                        }
                        // TODO vhdr.data->VHDR.volume ?
                }

                return 1;

        case ID_AIFF:
                ZEROIZE(comm);
                ZEROIZE(ssnd);
                ZEROIZE(name);
                ZEROIZE(auth);
                ZEROIZE(anno);

                while (iff_chunk_read(&chunk, data, length, &pos)) {
                        switch (chunk.id) {
                                case ID_COMM: comm = chunk; break;
                                case ID_SSND: ssnd = chunk; break;
                                case ID_NAME: name = chunk; break;
                                default: break;
                        }
                }
                if (!(comm.id && ssnd.id))
                        return 0;

                if (file) {
                        file->description = "Audio IFF sample";
                        file->type = TYPE_SAMPLE_PLAIN;
                }
                if (!name.id) name = auth;
                if (!name.id) name = anno;
                if (name.id) {
                        if (file) {
                                file->title = calloc(1, name.size + 1);
                                memcpy(file->title, name.data->bytes, name.size);
                                file->title[name.size] = '\0';
                        }
                        if (smp) {
                                int len = MIN(25, name.size);
                                memcpy(smp->name, name.data->bytes, len);
                                smp->name[len] = 0;
                        }
                }

                /* TODO loop points */

                if (smp) {
                        uint32_t flags = SF_BE | SF_PCMS;

                        switch (bswapBE16(comm.data->COMM.num_channels)) {
                        default:
                                log_appendf(4, "warning: multichannel AIFF is unsupported");
                        case 1:
                                flags |= SF_M;
                                break;
                        case 2:
                                flags |= SF_SI;
                                break;
                        }

                        switch ((bswapBE16(comm.data->COMM.sample_size) + 7) & ~7) {
                        default:
                                log_appendf(4, "warning: AIFF has unsupported bit-width");
                        case 8:
                                flags |= SF_8;
                                break;
                        case 16:
                                flags |= SF_16;
                                break;
                        }

                        // TODO: data checking; make sure sample count and byte size agree
                        // (and if not, cut to shorter of the two)

                        smp->c5speed = ConvertFromIeeeExtended(comm.data->COMM.sample_rate);
                        smp->length = bswapBE32(comm.data->COMM.num_frames);
                        smp->volume = 64*4;
                        smp->global_volume = 64;

                        // the audio data starts 8 bytes into the chunk
                        // (don't care about the block alignment stuff)
                        csf_read_sample(smp, flags, ssnd.data->bytes + 8, ssnd.size - 8);
                }

                return 1;
        }

        return 0;
}
Example #3
0
static int aiff_header(disko_t *fp, int bits, int channels, int rate,
        const char *name, size_t length, struct aiff_writedata *awd /* out */)
{
        int16_t s;
        uint32_t ul;
        int tlen, bps = 1;
        uint8_t b[10];

        bps *= ((bits + 7) / 8);
        /* note: channel multiply is done below -- need single-channel value for the COMM chunk */

        /* write a very large size for now */
        disko_write(fp, "FORM\377\377\377\377AIFF", 12);

        if (name && *name) {
                disko_write(fp, "NAME", 4);
                tlen = strlen(name);
                ul = (tlen + 1) & ~1; /* must be even */
                ul = bswapBE32(ul);
                disko_write(fp, &ul, 4);
                disko_write(fp, name, tlen);
                if (tlen & 1)
                        disko_putc(fp, '\0');
        }

        /* Common Chunk
                The Common Chunk describes fundamental parameters of the sampled sound.
        typedef struct {
                ID              ckID;           // 'COMM'
                long            ckSize;         // 18
                short           numChannels;
                unsigned long   numSampleFrames;
                short           sampleSize;
                extended        sampleRate;
        } CommonChunk; */
        disko_write(fp, "COMM", 4);
        ul = bswapBE32(18); /* chunk size -- won't change */
        disko_write(fp, &ul, 4);
        s = bswapBE16(channels);
        disko_write(fp, &s, 2);
        if (awd)
                awd->comm_frames = disko_tell(fp);
        ul = bswapBE32(length); /* num sample frames */
        disko_write(fp, &ul, 4);
        s = bswapBE16(bits);
        disko_write(fp, &s, 2);
        ConvertToIeeeExtended(rate, b);
        disko_write(fp, b, 10);

        /* NOW do this (sample size in AIFF is indicated per channel, not per frame) */
        bps *= channels; /* == number of bytes per (stereo) sample */

        /* Sound Data Chunk
                The Sound Data Chunk contains the actual sample frames.
        typedef struct {
                ID              ckID;           // 'SSND'
                long            ckSize;         // data size in bytes, *PLUS EIGHT* (for offset and blockSize)
                unsigned long   offset;         // just set this to 0...
                unsigned long   blockSize;      // likewise
                unsigned char   soundData[];
        } SoundDataChunk; */
        disko_write(fp, "SSND", 4);
        if (awd)
                awd->ssnd_size = disko_tell(fp);
        ul = bswapBE32(length * bps + 8);
        disko_write(fp, &ul, 4);
        ul = bswapBE32(0);
        disko_write(fp, &ul, 4);
        disko_write(fp, &ul, 4);

        return bps;
}
Example #4
0
int fmt_mod_load_song(song_t *song, slurp_t *fp, unsigned int lflags)
{
	uint8_t tag[4];
	int n, npat, pat, chan, nchan, nord;
	song_note_t *note;
	uint16_t tmp;
	int startrekker = 0;
	int test_wow = 0;
	int mk = 0;
	int maybe_st3 = 0;
	int maybe_ft2 = 0;
	uint8_t restart;
	long samplesize = 0;
	const char *tid = NULL;

	/* check the tag (and set the number of channels) -- this is ugly, so don't look */
	slurp_seek(fp, 1080, SEEK_SET);
	slurp_read(fp, tag, 4);
	if (!memcmp(tag, "M.K.", 4)) {
		/* M.K. = Protracker etc., or Mod's Grave (*.wow) */
		nchan = 4;
		test_wow = 1;
		mk = 1;
		maybe_ft2 = 1;
		tid = "Amiga-NewTracker";
	} else if (!memcmp(tag, "M!K!", 4)) {
		nchan = 4;
		tid = "Amiga-ProTracker";
	} else if (!memcmp(tag, "M&K!", 4) || !memcmp(tag, "N.T.", 4)) {
		nchan = 4;
		tid = "Amiga-NoiseTracker"; // or so the word on the street is; I don't have any of these
	} else if ((!memcmp(tag, "FLT", 3) || !memcmp(tag, "EXO", 3)) && (tag[3] == '4' || tag[3] == '8')) {
		// Hopefully EXO8 is stored the same way as FLT8
		nchan = tag[3] - '0';
		startrekker = (nchan == 8);
		tid = "%d Channel Startrekker";
		//log_appendf(4, " Warning: Startrekker AM synth is not supported");
	} else if (!memcmp(tag, "FEST", 4)) {
		// the mysterious mod.jobbig
		nchan = 4;
		tid = "4 Channel Startrekker (?)";
	} else if (!memcmp(tag, "OCTA", 4)) {
		nchan = 8;
		tid = "Amiga Oktalyzer"; // IT just identifies this as "8 Channel MOD"
	} else if (!memcmp(tag, "CD81", 4)) {
		nchan = 8;
		tid = "8 Channel Falcon"; // Atari Oktalyser
	} else if (tag[0] > '0' && tag[0] <= '9' && !memcmp(tag + 1, "CHN", 3)) {
		/* nCHN = Fast Tracker (if n is even) or TakeTracker (if n = 5, 7, or 9) */
		nchan = tag[0] - '0';
		if (nchan == 5 || nchan == 7 || nchan == 9) {
			tid = "%d Channel TakeTracker";
		} else {
			if (!(nchan & 1))
				maybe_ft2 = 1;
			tid = "%d Channel MOD"; // generic
		}
		maybe_st3 = 1;
	} else if (tag[0] > '0' && tag[0] <= '9' && tag[1] >= '0' && tag[1] <= '9'
		   && tag[2] == 'C' && (tag[3] == 'H' || tag[3] == 'N')) {
		/* nnCH = Fast Tracker (if n is even and <= 32) or TakeTracker (if n = 11, 13, 15)
		 * Not sure what the nnCN variant is. */
		nchan = 10 * (tag[0] - '0') + (tag[1] - '0');
		if (nchan == 11 || nchan == 13 || nchan == 15) {
			tid = "%d Channel TakeTracker";
		} else {
			if ((nchan & 1) == 0 && nchan <= 32 && tag[3] == 'H')
				maybe_ft2 = 1;
			tid = "%d Channel MOD"; // generic
		}
		if (tag[3] == 'H')
			maybe_st3 = 1;
	} else if (!memcmp(tag, "TDZ", 3) && tag[3] > '0' && tag[3] <= '9') {
		/* TDZ[1-3] = TakeTracker */
		nchan = tag[3] - '0';
		if (nchan < 4)
			tid = "%d Channel TakeTracker";
		else
			tid = "%d Channel MOD";
	} else {
		return LOAD_UNSUPPORTED;
	}

	/* suppose the tag is 90CH :) */
	if (nchan > 64) {
		//fprintf(stderr, "%s: Too many channels!\n", filename);
		return LOAD_FORMAT_ERROR;
	}

	/* read the title */
	slurp_rewind(fp);
	slurp_read(fp, song->title, 20);
	song->title[20] = 0;

	/* sample headers */
	for (n = 1; n < 32; n++) {
		slurp_read(fp, song->samples[n].name, 22);
		song->samples[n].name[22] = 0;

		slurp_read(fp, &tmp, 2);
		song->samples[n].length = bswapBE16(tmp) * 2;

		/* this is only necessary for the wow test... */
		samplesize += song->samples[n].length;

		song->samples[n].c5speed = MOD_FINETUNE(slurp_getc(fp));

		song->samples[n].volume = slurp_getc(fp);
		if (song->samples[n].volume > 64)
			song->samples[n].volume = 64;
		if (!song->samples[n].length && song->samples[n].volume)
			maybe_ft2 = 0;
		song->samples[n].volume *= 4; //mphack
		song->samples[n].global_volume = 64;

		slurp_read(fp, &tmp, 2);
		song->samples[n].loop_start = bswapBE16(tmp) * 2;
		slurp_read(fp, &tmp, 2);
		tmp = bswapBE16(tmp) * 2;
		if (tmp > 2)
			song->samples[n].flags |= CHN_LOOP;
		else if (tmp == 0)
			maybe_st3 = 0;
		else if (!song->samples[n].length)
			maybe_ft2 = 0;
		song->samples[n].loop_end = song->samples[n].loop_start + tmp;
		song->samples[n].vib_type = 0;
		song->samples[n].vib_rate = 0;
		song->samples[n].vib_depth = 0;
		song->samples[n].vib_speed = 0;
	}

	/* pattern/order stuff */
	nord = slurp_getc(fp);
	restart = slurp_getc(fp);

	slurp_read(fp, song->orderlist, 128);
	npat = 0;
	if (startrekker) {
		/* from mikmod: if the file says FLT8, but the orderlist
		has odd numbers, it's probably really an FLT4 */
		for (n = 0; n < 128; n++) {
			if (song->orderlist[n] & 1) {
				startrekker = 0;
				nchan = 4;
				break;
			}
		}
	}
	if (startrekker) {
		for (n = 0; n < 128; n++)
			song->orderlist[n] >>= 1;
	}
	for (n = 0; n < 128; n++) {
		if (song->orderlist[n] >= MAX_PATTERNS)
			song->orderlist[n] = ORDER_SKIP;
		else if (song->orderlist[n] > npat)
			npat = song->orderlist[n];
	}
	/* set all the extra orders to the end-of-song marker */
	memset(song->orderlist + nord, ORDER_LAST, MAX_ORDERS - nord);

	if (restart == 0x7f && maybe_st3)
		tid = "Scream Tracker 3?";
	else if (restart == 0x7f && mk)
		tid = "%d Channel ProTracker";
	else if (restart <= npat && maybe_ft2)
		tid = "%d Channel FastTracker";
	else if (restart == npat && mk)
		tid = "%d Channel Soundtracker";

	/* hey, is this a wow file? */
	if (test_wow) {
		slurp_seek(fp, 0, SEEK_END);
		if (slurp_tell(fp) == 2048 * npat + samplesize + 3132) {
			nchan = 8;
			tid = "Mod's Grave WOW";
		}
	}


	// http://llvm.org/viewvc/llvm-project?view=rev&revision=91888
	sprintf(song->tracker_id, tid ? tid : "%d Channel MOD", nchan);
	slurp_seek(fp, 1084, SEEK_SET);

	/* pattern data */
	if (startrekker) {
		for (pat = 0; pat <= npat; pat++) {
			note = song->patterns[pat] = csf_allocate_pattern(64);
			song->pattern_size[pat] = song->pattern_alloc_size[pat] = 64;
			for (n = 0; n < 64; n++, note += 60) {
				for (chan = 0; chan < 4; chan++, note++) {
					uint8_t p[4];
					slurp_read(fp, p, 4);
					mod_import_note(p, note);
					csf_import_mod_effect(note, 0);
				}
			}
			note = song->patterns[pat] + 4;
			for (n = 0; n < 64; n++, note += 60) {
				for (chan = 0; chan < 4; chan++, note++) {
					uint8_t p[4];
					slurp_read(fp, p, 4);
					mod_import_note(p, note);
					csf_import_mod_effect(note, 0);
				}
			}
		}
	} else {
		for (pat = 0; pat <= npat; pat++) {
			note = song->patterns[pat] = csf_allocate_pattern(64);
			song->pattern_size[pat] = song->pattern_alloc_size[pat] = 64;
			for (n = 0; n < 64; n++, note += 64 - nchan) {
				for (chan = 0; chan < nchan; chan++, note++) {
					uint8_t p[4];
					slurp_read(fp, p, 4);
					mod_import_note(p, note);
					csf_import_mod_effect(note, 0);
				}
			}
		}
	}
	if (restart < npat)
		csf_insert_restart_pos(song, restart);

	/* sample data */
	if (!(lflags & LOAD_NOSAMPLES)) {
		for (n = 1; n < 32; n++) {
			uint32_t ssize;

			if (song->samples[n].length == 0)
				continue;
			ssize = csf_read_sample(song->samples + n, SF_8 | SF_M | SF_LE | SF_PCMS,
				fp->data + fp->pos, fp->length - fp->pos);
			slurp_seek(fp, ssize, SEEK_CUR);
		}
	}

	/* set some other header info that's always the same for .mod files */
	song->flags = (SONG_ITOLDEFFECTS | SONG_COMPATGXX);
	for (n = 0; n < nchan; n++)
		song->channels[n].panning = PROTRACKER_PANNING(n);
	for (; n < MAX_CHANNELS; n++)
		song->channels[n].flags = CHN_MUTE;

	song->pan_separation = 64;

//      if (slurp_error(fp)) {
//              return LOAD_FILE_ERROR;
//      }

	/* done! */
	return LOAD_SUCCESS;
}