From 78f8fce7f286fd0c71774e2567404ed51f24fef3 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Fri, 26 May 2017 21:51:04 -0700 Subject: *: initial commit of stripped schism stuff Forking schism tracker's IT playback stuff into a little playback library for embedding in demos. --- src/player/csndfile.c | 1094 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1094 insertions(+) create mode 100644 src/player/csndfile.c (limited to 'src/player/csndfile.c') diff --git a/src/player/csndfile.c b/src/player/csndfile.c new file mode 100644 index 0000000..bba8ae0 --- /dev/null +++ b/src/player/csndfile.c @@ -0,0 +1,1094 @@ +/* + * Schism Tracker - a cross-platform Impulse Tracker clone + * copyright (c) 2003-2005 Storlek + * copyright (c) 2005-2008 Mrs. Brisby + * copyright (c) 2009 Storlek & Mrs. Brisby + * copyright (c) 2010-2012 Storlek + * URL: http://schismtracker.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define NEED_BYTESWAP +#define NEED_TIME +#include "headers.h" + +#include +#include +#include + +#include "sndfile.h" +#include "log.h" +#include "util.h" +#include "fmt.h" // for it_decompress8 / it_decompress16 + + +static void _csf_reset(song_t *csf) +{ + unsigned int i; + + csf->flags = 0; + csf->pan_separation = 128; + csf->num_voices = 0; + csf->freq_factor = csf->tempo_factor = 128; + csf->initial_global_volume = 128; + csf->current_global_volume = 128; + csf->initial_speed = 6; + csf->initial_tempo = 125; + csf->process_row = 0; + csf->row = 0; + csf->current_pattern = 0; + csf->current_order = 0; + csf->process_order = 0; + csf->mixing_volume = 0x30; + memset(csf->message, 0, sizeof(csf->message)); + + csf->row_highlight_major = 16; + csf->row_highlight_minor = 4; + + /* This is intentionally crappy quality, so that it's very obvious if it didn't get initialized */ + csf->mix_flags = 0; + csf_set_wave_config(csf, 4000, 8, 1); + + memset(csf->voices, 0, sizeof(csf->voices)); + memset(csf->voice_mix, 0, sizeof(csf->voice_mix)); + memset(csf->samples, 0, sizeof(csf->samples)); + memset(csf->instruments, 0, sizeof(csf->instruments)); + memset(csf->orderlist, 0xFF, sizeof(csf->orderlist)); + memset(csf->patterns, 0, sizeof(csf->patterns)); + + csf_reset_midi_cfg(csf); + csf_forget_history(csf); + + for (i = 0; i < MAX_PATTERNS; i++) { + csf->pattern_size[i] = 64; + csf->pattern_alloc_size[i] = 64; + } + for (i = 0; i < MAX_SAMPLES; i++) { + csf->samples[i].c5speed = 8363; + csf->samples[i].volume = 64 * 4; + csf->samples[i].global_volume = 64; + } + for (i = 0; i < MAX_CHANNELS; i++) { + csf->channels[i].panning = 128; + csf->channels[i].volume = 64; + csf->channels[i].flags = 0; + } +} + +////////////////////////////////////////////////////////// +// song_t + +song_t *csf_allocate(void) +{ + song_t *csf = calloc(1, sizeof(song_t)); + _csf_reset(csf); + return csf; +} + +void csf_free(song_t *csf) +{ + if (csf) { + csf_destroy(csf); + free(csf); + } +} + + +static void _init_envelope(song_envelope_t *env, int n) +{ + env->nodes = 2; + env->ticks[0] = 0; + env->ticks[1] = 100; + env->values[0] = n; + env->values[1] = n; +} + +void csf_init_instrument(song_instrument_t *ins, int samp) +{ + int n; + _init_envelope(&ins->vol_env, 64); + _init_envelope(&ins->pan_env, 32); + _init_envelope(&ins->pitch_env, 32); + ins->global_volume = 128; + ins->panning = 128; + ins->midi_bank = -1; + ins->midi_program = -1; + ins->pitch_pan_center = 60; // why does pitch/pan not use the same note values as everywhere else?! + for (n = 0; n < 128; n++) { + ins->sample_map[n] = samp; + ins->note_map[n] = n + 1; + } +} + +song_instrument_t *csf_allocate_instrument(void) +{ + song_instrument_t *ins = calloc(1, sizeof(song_instrument_t)); + csf_init_instrument(ins, 0); + return ins; +} + +void csf_free_instrument(song_instrument_t *i) +{ + free(i); +} + + +void csf_destroy(song_t *csf) +{ + int i; + + for (i = 0; i < MAX_PATTERNS; i++) { + if (csf->patterns[i]) { + csf_free_pattern(csf->patterns[i]); + csf->patterns[i] = NULL; + } + } + for (i = 1; i < MAX_SAMPLES; i++) { + song_sample_t *pins = &csf->samples[i]; + if (pins->data) { + csf_free_sample(pins->data); + pins->data = NULL; + } + } + for (i = 0; i < MAX_INSTRUMENTS; i++) { + if (csf->instruments[i]) { + csf_free_instrument(csf->instruments[i]); + csf->instruments[i] = NULL; + } + } + + _csf_reset(csf); +} + +song_note_t *csf_allocate_pattern(uint32_t rows) +{ + return calloc(rows * MAX_CHANNELS, sizeof(song_note_t)); +} + +void csf_free_pattern(void *pat) +{ + free(pat); +} + +/* Note: this function will appear in valgrind to be a sieve for memory leaks. +It isn't; it's just being confused by the adjusted pointer being stored. */ +signed char *csf_allocate_sample(uint32_t nbytes) +{ + signed char *p = calloc(1, (nbytes + 39) & ~7); // magic + if (p) + p += 16; + return p; +} + +void csf_free_sample(void *p) +{ + if (p) + free(p - 16); +} + +void csf_forget_history(song_t *csf) +{ + free(csf->histdata); + csf->histdata = NULL; + csf->histlen = 0; + gettimeofday(&csf->editstart, NULL); +} + +/* --------------------------------------------------------------------------------------------------------- */ +/* Counting and checking stuff. */ + +static int name_is_blank(char *name) +{ + int n; + for (n = 0; n < 25; n++) { + if (name[n] != '\0' && name[n] != ' ') + return 0; + } + return 1; +} + +const song_note_t blank_pattern[64 * 64]; +const song_note_t *blank_note = blank_pattern; // Same thing, really. + +int csf_note_is_empty(song_note_t *note) +{ + return !memcmp(note, blank_pattern, sizeof(song_note_t)); +} + +int csf_pattern_is_empty(song_t *csf, int n) +{ + if (!csf->patterns[n]) + return 1; + if (csf->pattern_size[n] != 64) + return 0; + return !memcmp(csf->patterns[n], blank_pattern, sizeof(blank_pattern)); +} + +int csf_sample_is_empty(song_sample_t *smp) +{ + return (smp->data == NULL + && name_is_blank(smp->name) + && smp->filename[0] == '\0' + && smp->c5speed == 8363 + && smp->volume == 64*4 //mphack + && smp->global_volume == 64 + && smp->panning == 0 + && !(smp->flags & (CHN_LOOP | CHN_SUSTAINLOOP | CHN_PANNING)) + && smp->length == 0 + && smp->loop_start == 0 + && smp->loop_end == 0 + && smp->sustain_start == 0 + && smp->sustain_end == 0 + && smp->vib_type == VIB_SINE + && smp->vib_rate == 0 + && smp->vib_depth == 0 + && smp->vib_speed == 0 + ); +} + +static int env_is_blank(song_envelope_t *env, int value) +{ + return (env->nodes == 2 + && env->loop_start == 0 + && env->loop_end == 0 + && env->sustain_start == 0 + && env->sustain_end == 0 + && env->ticks[0] == 0 + && env->ticks[1] == 100 + && env->values[0] == value + && env->values[1] == value + ); +} + +int csf_instrument_is_empty(song_instrument_t *ins) +{ + int n; + if (!ins) + return 1; + + for (n = 0; n < NOTE_LAST - NOTE_FIRST; n++) { + if (ins->sample_map[n] != 0 || ins->note_map[n] != (n + NOTE_FIRST)) + return 0; + } + return (name_is_blank(ins->name) + && ins->filename[0] == '\0' + && ins->flags == 0 /* No envelopes, loop points, panning, or carry flags set */ + && ins->nna == NNA_NOTECUT + && ins->dct == DCT_NONE + && ins->dca == DCA_NOTECUT + && env_is_blank(&ins->vol_env, 64) + && ins->global_volume == 128 + && ins->fadeout == 0 + && ins->vol_swing == 0 + && env_is_blank(&ins->pan_env, 32) + && ins->panning == 32*4 //mphack + && ins->pitch_pan_center == 60 // C-5 (blah) + && ins->pitch_pan_separation == 0 + && ins->pan_swing == 0 + && env_is_blank(&ins->pitch_env, 32) + && ins->ifc == 0 + && ins->ifr == 0 + && ins->midi_channel_mask == 0 + && ins->midi_program == -1 + && ins->midi_bank == -1 + ); +} + +// IT-compatible: last order of "main song", or 0 +int csf_last_order(song_t *csf) +{ + int n = 0; + while (n < MAX_ORDERS && csf->orderlist[n] != ORDER_LAST) + n++; + return n ? n - 1 : 0; +} + +// Total count of orders in orderlist before end of data +int csf_get_num_orders(song_t *csf) +{ + int n = MAX_ORDERS; + while (n >= 0 && csf->orderlist[--n] == ORDER_LAST) { + } + return n + 1; +} + +// Total number of non-empty patterns in song, according to csf_pattern_is_empty +int csf_get_num_patterns(song_t *csf) +{ + int n = MAX_PATTERNS - 1; + while (n && csf_pattern_is_empty(csf, n)) + n--; + return n+ 1; +} + +int csf_get_num_samples(song_t *csf) +{ + int n = MAX_SAMPLES - 1; + while (n > 0 && csf_sample_is_empty(csf->samples + n)) + n--; + return n; +} + +int csf_get_num_instruments(song_t *csf) +{ + int n = MAX_INSTRUMENTS - 1; + while (n > 0 && csf_instrument_is_empty(csf->instruments[n])) + n--; + return n; +} + + +int csf_first_blank_sample(song_t *csf, int start) +{ + int n; + for (n = MAX(start, 1); n < MAX_SAMPLES; n++) { + if (csf_sample_is_empty(csf->samples + n)) + return n; + } + return -1; +} + +int csf_first_blank_instrument(song_t *csf, int start) +{ + int n; + for (n = MAX(start, 1); n < MAX_INSTRUMENTS; n++) { + if (csf_instrument_is_empty(csf->instruments[n])) + return n; + } + return -1; +} + + +////////////////////////////////////////////////////////////////////////// +// Misc functions + +midi_config_t default_midi_config; + + +void csf_reset_midi_cfg(song_t *csf) +{ + memcpy(&csf->midi_config, &default_midi_config, sizeof(default_midi_config)); +} + +void csf_copy_midi_cfg(song_t *dest, song_t *src) +{ + memcpy(&dest->midi_config, &src->midi_config, sizeof(midi_config_t)); +} + + +int csf_set_wave_config(song_t *csf, uint32_t rate,uint32_t bits,uint32_t channels) +{ + int reset = ((csf->mix_frequency != rate) + || (csf->mix_bits_per_sample != bits) + || (csf->mix_channels != channels)); + csf->mix_channels = channels; + csf->mix_frequency = rate; + csf->mix_bits_per_sample = bits; + csf_init_player(csf, reset); + return 1; +} + + +int csf_set_resampling_mode(song_t *csf, uint32_t mode) +{ + uint32_t d = csf->mix_flags & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + switch(mode) { + case SRCMODE_NEAREST: d |= SNDMIX_NORESAMPLING; break; + case SRCMODE_LINEAR: break; + case SRCMODE_SPLINE: d |= SNDMIX_HQRESAMPLER; break; + case SRCMODE_POLYPHASE: d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); break; + default: return 0; + } + csf->mix_flags = d; + return 1; +} + + +// This used to use some retarded positioning based on the total number of rows elapsed, which is useless. +// However, the only code calling this function is in this file, to set it to the start, so I'm optimizing +// out the row count. +static void set_current_pos_0(song_t *csf) +{ + song_voice_t *v = csf->voices; + for (uint32_t i = 0; i < MAX_VOICES; i++, v++) { + memset(v, 0, sizeof(*v)); + v->cutoff = 0x7F; + v->volume = 256; + if (i < MAX_CHANNELS) { + v->panning = csf->channels[i].panning; + v->global_volume = csf->channels[i].volume; + v->flags = csf->channels[i].flags; + } else { + v->panning = 128; + v->global_volume = 64; + } + } + csf->current_global_volume = csf->initial_global_volume; + csf->current_speed = csf->initial_speed; + csf->current_tempo = csf->initial_tempo; +} + + +void csf_set_current_order(song_t *csf, uint32_t position) +{ + for (uint32_t j = 0; j < MAX_VOICES; j++) { + song_voice_t *v = csf->voices + j; + + v->period = 0; + v->note = v->new_note = v->new_instrument = 0; + v->portamento_target = 0; + v->n_command = 0; + v->cd_patloop = 0; + v->patloop_row = 0; + v->cd_tremor = 0; + // modplug sets vib pos to 16 in old effects mode for some reason *shrug* + v->vibrato_position = (csf->flags & SONG_ITOLDEFFECTS) ? 0 : 0x10; + v->tremolo_position = 0; + } + if (position > MAX_ORDERS) + position = 0; + if (!position) + set_current_pos_0(csf); + + csf->process_order = position - 1; + csf->process_row = PROCESS_NEXT_ORDER; + csf->row = 0; + csf->break_row = 0; /* set this to whatever row to jump to */ + csf->tick_count = 1; + csf->row_count = 0; + csf->buffer_count = 0; + + csf->flags &= ~(SONG_PATTERNLOOP|SONG_ENDREACHED); +} + +void csf_reset_playmarks(song_t *csf) +{ + int n; + + for (n = 1; n < MAX_SAMPLES; n++) { + csf->samples[n].played = 0; + } + for (n = 1; n < MAX_INSTRUMENTS; n++) { + if (csf->instruments[n]) + csf->instruments[n]->played = 0; + } +} + + +/* --------------------------------------------------------------------------------------------------------- */ + +#define SF_FAIL(name, n) \ + ({ log_appendf(4, "%s: internal error: unsupported %s %d", __FUNCTION__, name, n); return 0; }) + + +uint32_t csf_read_sample(song_sample_t *sample, uint32_t flags, const void *filedata, uint32_t memsize) +{ + uint32_t len = 0, mem; + const char *buffer = (const char *) filedata; + + if (sample->flags & CHN_ADLIB) return 0; // no sample data + + if (!sample || sample->length < 1 || !buffer) return 0; + + // validate the read flags before anything else + switch (flags & SF_BIT_MASK) { + case SF_7: case SF_8: case SF_16: case SF_24: case SF_32: break; + default: SF_FAIL("bit width", flags & SF_BIT_MASK); + } + switch (flags & SF_CHN_MASK) { + case SF_M: case SF_SI: case SF_SS: break; + default: SF_FAIL("channel mask", flags & SF_CHN_MASK); + } + switch (flags & SF_END_MASK) { + case SF_LE: case SF_BE: break; + default: SF_FAIL("endianness", flags & SF_END_MASK); + } + switch (flags & SF_ENC_MASK) { + case SF_PCMS: case SF_PCMU: case SF_PCMD: case SF_IT214: case SF_IT215: + case SF_AMS: case SF_DMF: case SF_MDL: case SF_PTM: + break; + default: SF_FAIL("encoding", flags & SF_ENC_MASK); + } + if ((flags & ~(SF_BIT_MASK | SF_CHN_MASK | SF_END_MASK | SF_ENC_MASK)) != 0) { + SF_FAIL("extra flag", flags & ~(SF_BIT_MASK | SF_CHN_MASK | SF_END_MASK | SF_ENC_MASK)); + } + + if (sample->length > MAX_SAMPLE_LENGTH) sample->length = MAX_SAMPLE_LENGTH; + mem = sample->length+6; + sample->flags &= ~(CHN_16BIT|CHN_STEREO); + switch (flags & SF_BIT_MASK) { + case SF_16: case SF_24: case SF_32: + // these are all stuffed into 16 bits. + mem *= 2; + sample->flags |= CHN_16BIT; + } + switch (flags & SF_CHN_MASK) { + case SF_SI: case SF_SS: + mem *= 2; + sample->flags |= CHN_STEREO; + } + if ((sample->data = csf_allocate_sample(mem)) == NULL) { + sample->length = 0; + return 0; + } + switch(flags) { + // 1: 8-bit unsigned PCM data + case RS_PCM8U: + { + len = sample->length; + if (len > memsize) len = sample->length = memsize; + signed char *data = sample->data; + for (uint32_t j=0; jlength; + if (len > memsize) break; + signed char *data = sample->data; + const signed char *p = (const signed char *)buffer; + int delta = 0; + for (uint32_t j=0; jlength * 2; + if (len > memsize) break; + short *data = (short *)sample->data; + short *p = (short *)buffer; + unsigned short tmp; + int delta16 = 0; + for (uint32_t j=0; jlength * 2; + if (len <= memsize) memcpy(sample->data, buffer, len); + short int *data = (short int *)sample->data; + for (uint32_t j=0; jlength * 2; + if (len > memsize) len = memsize & ~1; + if (len > 1) { + signed char *data = (signed char *)sample->data; + signed char *src = (signed char *)buffer; + for (uint32_t j=0; jlength * 2; + if (len <= memsize) memcpy(sample->data, buffer, len); + short int *data = (short int *)sample->data; + for (uint32_t j=0; jlength * 2; + if (len*2 <= memsize) { + signed char *data = (signed char *)sample->data; + signed char *src = (signed char *)buffer; + for (uint32_t j=0; jlength; + signed char *psrc = (signed char *)buffer; + signed char *data = (signed char *)sample->data; + if (len*2 > memsize) break; + for (uint32_t j=0; jlength; + short int *psrc = (short int *)buffer; + short int *data = (short int *)sample->data; + if (len*4 > memsize) break; + for (uint32_t j=0; jdata, sample->length, + buffer, memsize, (flags == RS_IT2158), 1); + } else { + it_decompress16(sample->data, sample->length, + buffer, memsize, (flags == RS_IT21516), 1); + } + break; + case RS_IT2148S: + case RS_IT21416S: + case RS_IT2158S: + case RS_IT21516S: + len = memsize; + if (len < 4) break; + if (flags == RS_IT2148S || flags == RS_IT2158S) { + uint32_t offset = it_decompress8(sample->data, sample->length, + buffer, memsize, (flags == RS_IT2158S), 2); + it_decompress8(sample->data + 1, sample->length, + buffer + offset, memsize - offset, (flags == RS_IT2158S), 2); + } else { + uint32_t offset = it_decompress16(sample->data, sample->length, + buffer, memsize, (flags == RS_IT21516S), 2); + it_decompress16(sample->data + 2, sample->length, + buffer + offset, memsize - offset, (flags == RS_IT21516S), 2); + } + break; + + // 8-bit interleaved stereo samples + case RS_STIPCM8S: + case RS_STIPCM8U: + { + int iadd = 0; + if (flags == RS_STIPCM8U) { iadd = -0x80; } + len = sample->length; + if (len*2 > memsize) len = memsize >> 1; + uint8_t * psrc = (uint8_t *)buffer; + uint8_t * data = (uint8_t *)sample->data; + for (uint32_t j=0; jlength; + if (len*4 > memsize) len = memsize >> 2; + short int *psrc = (short int *)buffer; + short int *data = (short int *)sample->data; + for (uint32_t j=0; j 9) { + const char *psrc = buffer; + char packcharacter = buffer[8], *pdest = (char *)sample->data; + len += bswapLE32(*((uint32_t *)(buffer+4))); + if (len > memsize) len = memsize; + uint32_t dmax = sample->length; + if (sample->flags & CHN_16BIT) dmax <<= 1; + AMSUnpack(psrc+9, len-9, pdest, dmax, packcharacter); + } + break; +#endif + + // PTM 8bit delta to 16-bit sample + case RS_PTM8DTO16: + { + len = sample->length * 2; + if (len > memsize) break; + signed char *data = (signed char *)sample->data; + signed char delta8 = 0; + for (uint32_t j=0; jdata; + for (uint32_t j=0; j= 8) { + // first 4 bytes indicate packed length + len = bswapLE32(*((uint32_t *) buffer)); + len = MIN(len, memsize) + 4; + uint8_t * data = (uint8_t *)sample->data; + uint8_t * ibuf = (uint8_t *)(buffer + 4); + uint32_t bitbuf = bswapLE32(*((uint32_t *)ibuf)); + uint32_t bitnum = 32; + uint8_t dlt = 0, lowbyte = 0; + ibuf += 4; + // TODO move all this junk to fmt/compression.c + for (uint32_t j=0; jlength; j++) { + uint8_t hibyte; + uint8_t sign; + if (flags == RS_MDL16) + lowbyte = (uint8_t)mdl_read_bits(&bitbuf, &bitnum, &ibuf, 8); + sign = (uint8_t)mdl_read_bits(&bitbuf, &bitnum, &ibuf, 1); + if (mdl_read_bits(&bitbuf, &bitnum, &ibuf, 1)) { + hibyte = (uint8_t)mdl_read_bits(&bitbuf, &bitnum, &ibuf, 3); + } else { + hibyte = 8; + while (!mdl_read_bits(&bitbuf, &bitnum, &ibuf, 1)) hibyte += 0x10; + hibyte += mdl_read_bits(&bitbuf, &bitnum, &ibuf, 4); + } + if (sign) hibyte = ~hibyte; + dlt += hibyte; + if (flags == RS_MDL8) { + data[j] = dlt; + } else { +#ifdef WORDS_BIGENDIAN + data[j<<1] = dlt; + data[(j<<1)+1] = lowbyte; +#else + data[j<<1] = lowbyte; + data[(j<<1)+1] = dlt; +#endif + } + } + } + break; + +#if 0 + case RS_DMF8: + case RS_DMF16: + len = memsize; + if (len >= 4) { + uint32_t maxlen = sample->length; + if (sample->flags & CHN_16BIT) maxlen <<= 1; + uint8_t * ibuf = (uint8_t *)buffer; + uint8_t * ibufmax = (uint8_t *)(buffer+memsize); + len = DMFUnpack((uint8_t *)sample->data, ibuf, ibufmax, maxlen); + } + break; +#endif + + // PCM 24-bit signed -> load sample, and normalize it to 16-bit + case RS_PCM24S: + case RS_PCM32S: + len = sample->length * 3; + if (flags == RS_PCM32S) len += sample->length; + if (len > memsize) break; + if (len > 4*8) { + uint32_t slsize = (flags == RS_PCM32S) ? 4 : 3; + uint8_t * src = (uint8_t *)buffer; + int32_t max = 255; + if (flags == RS_PCM32S) src++; + for (uint32_t j=0; j max) max = l; + if (-l > max) max = -l; + } + max = (max / 128) + 1; + signed short *dest = (signed short *)sample->data; + for (uint32_t k=0; k load sample, and normalize it to 16-bit + case RS_STIPCM24S: + case RS_STIPCM32S: + len = sample->length * 6; + if (flags == RS_STIPCM32S) len += sample->length * 2; + if (len > memsize) break; + if (len > 8*8) { + uint32_t slsize = (flags == RS_STIPCM32S) ? 4 : 3; + uint8_t * src = (uint8_t *)buffer; + int32_t max = 255; + if (flags == RS_STIPCM32S) src++; + for (uint32_t j=0; j max) max = l; + if (-l > max) max = -l; + } + max = (max / 128) + 1; + signed short *dest = (signed short *)sample->data; + for (uint32_t k=0; klength; + if (len*4 > memsize) len = memsize >> 2; + const uint8_t * psrc = (const uint8_t *)buffer; + short int *data = (short int *)sample->data; + for (uint32_t j=0; jflags &= ~(CHN_16BIT | CHN_STEREO); + len = sample->length = MIN(sample->length, memsize); + for (uint32_t j = 0; j < len; j++) + sample->data[j] = CLAMP(buffer[j] * 2, -128, 127); + break; + + // Default: 8-bit signed PCM data + default: + printf("DEFAULT: %d\n", flags); + case SF(8,M,BE,PCMS): /* endianness is irrelevant for 8-bit samples */ + case SF(8,M,LE,PCMS): + sample->flags &= ~(CHN_16BIT | CHN_STEREO); + len = sample->length; + if (len > memsize) len = sample->length = memsize; + memcpy(sample->data, buffer, len); + break; + } + if (len > memsize) { + if (sample->data) { + sample->length = 0; + csf_free_sample(sample->data); + sample->data = NULL; + } + return 0; + } + csf_adjust_sample_loop(sample); + return len; +} + +/* --------------------------------------------------------------------------------------------------------- */ + +void csf_adjust_sample_loop(song_sample_t *sample) +{ + if (!sample->data) return; + if (sample->loop_end > sample->length) sample->loop_end = sample->length; + if (sample->loop_start+2 >= sample->loop_end) { + sample->loop_start = sample->loop_end = 0; + sample->flags &= ~CHN_LOOP; + } + + // poopy, removing all that loop-hacking code has produced... very nasty sounding loops! + // so I guess I should rewrite the crap at the end of the sample at least. + uint32_t len = sample->length; + if (sample->flags & CHN_16BIT) { + short int *data = (short int *)sample->data; + // Adjust end of sample + if (sample->flags & CHN_STEREO) { + data[len*2+6] + = data[len*2+4] + = data[len*2+2] + = data[len*2] + = data[len*2-2]; + data[len*2+7] + = data[len*2+5] + = data[len*2+3] + = data[len*2+1] + = data[len*2-1]; + } else { + data[len+4] + = data[len+3] + = data[len+2] + = data[len+1] + = data[len] + = data[len-1]; + } + } else { + signed char *data = sample->data; + // Adjust end of sample + if (sample->flags & CHN_STEREO) { + data[len*2+6] + = data[len*2+4] + = data[len*2+2] + = data[len*2] + = data[len*2-2]; + data[len*2+7] + = data[len*2+5] + = data[len*2+3] + = data[len*2+1] + = data[len*2-1]; + } else { + data[len+4] + = data[len+3] + = data[len+2] + = data[len+1] + = data[len] + = data[len-1]; + } + } +} + + +void csf_import_s3m_effect(song_note_t *m, int from_it) +{ + uint32_t effect = m->effect; + uint32_t param = m->param; + switch (effect + 0x40) + { + case 'A': effect = FX_SPEED; break; + case 'B': effect = FX_POSITIONJUMP; break; + case 'C': + effect = FX_PATTERNBREAK; + if (!from_it) + param = (param >> 4) * 10 + (param & 0x0F); + break; + case 'D': effect = FX_VOLUMESLIDE; break; + case 'E': effect = FX_PORTAMENTODOWN; break; + case 'F': effect = FX_PORTAMENTOUP; break; + case 'G': effect = FX_TONEPORTAMENTO; break; + case 'H': effect = FX_VIBRATO; break; + case 'I': effect = FX_TREMOR; break; + case 'J': effect = FX_ARPEGGIO; break; + case 'K': effect = FX_VIBRATOVOL; break; + case 'L': effect = FX_TONEPORTAVOL; break; + case 'M': effect = FX_CHANNELVOLUME; break; + case 'N': effect = FX_CHANNELVOLSLIDE; break; + case 'O': effect = FX_OFFSET; break; + case 'P': effect = FX_PANNINGSLIDE; break; + case 'Q': effect = FX_RETRIG; break; + case 'R': effect = FX_TREMOLO; break; + case 'S': + effect = FX_SPECIAL; + // convert old SAx to S8x + if (!from_it && ((param & 0xf0) == 0xa0)) + param = 0x80 | ((param & 0xf) ^ 8); + break; + case 'T': effect = FX_TEMPO; break; + case 'U': effect = FX_FINEVIBRATO; break; + case 'V': + effect = FX_GLOBALVOLUME; + if (!from_it) + param *= 2; + break; + case 'W': effect = FX_GLOBALVOLSLIDE; break; + case 'X': + effect = FX_PANNING; + if (!from_it) { + if (param == 0xa4) { + effect = FX_SPECIAL; + param = 0x91; + } else if (param > 0x7f) { + param = 0xff; + } else { + param *= 2; + } + } + break; + case 'Y': effect = FX_PANBRELLO; break; + case 'Z': effect = FX_MIDI; break; + default: effect = 0; + } + m->effect = effect; + m->param = param; +} -- cgit v1.2.3