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/sndmix.c | 1259 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1259 insertions(+) create mode 100644 src/player/sndmix.c (limited to 'src/player/sndmix.c') diff --git a/src/player/sndmix.c b/src/player/sndmix.c new file mode 100644 index 0000000..3fb8e07 --- /dev/null +++ b/src/player/sndmix.c @@ -0,0 +1,1259 @@ +/* + * 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 + */ + +#include "sndfile.h" +#include "snd_fm.h" +#include "cmixer.h" + +#include "util.h" /* for clamp */ + +// Volume ramp length, in 1/10 ms +#define VOLUMERAMPLEN 146 // 1.46ms = 64 samples at 44.1kHz + +// VU meter +#define VUMETER_DECAY 16 + +// SNDMIX: These are global flags for playback control +unsigned int max_voices = 32; // ITT it is 1994 + +// Mixing data initialized in +static unsigned int volume_ramp_samples = 64; +unsigned int global_vu_left = 0; +unsigned int global_vu_right = 0; +int32_t g_dry_rofs_vol = 0; +int32_t g_dry_lofs_vol = 0; + +typedef uint32_t (* convert_t)(void *, int *, uint32_t, int *, int *); + + +// see also csf_midi_out_raw in effects.c +void (*csf_midi_out_note)(int chan, const song_note_t *m) = NULL; + + +// The volume we have here is in range 0..(63*255) (0..16065) +// We should keep that range, but convert it into a logarithmic +// one such that a change of 256*8 (2048) corresponds to a halving +// of the volume. +// logvolume = 2^(linvolume / (4096/8)) * (4096/64) +// However, because the resolution of MIDI volumes +// is merely 128 units, we can use a lookup table. +// +// In this table, each value signifies the minimum value +// that volume must be in order for the result to be +// that table index. +static const unsigned short GMvolTransition[128] = +{ + 0, 2031, 4039, 5214, 6048, 6694, 7222, 7669, + 8056, 8397, 8702, 8978, 9230, 9462, 9677, 9877, +10064,10239,10405,10562,10710,10852,10986,11115, +11239,11357,11470,11580,11685,11787,11885,11980, +12072,12161,12248,12332,12413,12493,12570,12645, +12718,12790,12860,12928,12995,13060,13123,13186, +13247,13306,13365,13422,13479,13534,13588,13641, +13693,13745,13795,13844,13893,13941,13988,14034, +14080,14125,14169,14213,14256,14298,14340,14381, +14421,14461,14501,14540,14578,14616,14653,14690, +14727,14763,14798,14833,14868,14902,14936,14970, +15003,15035,15068,15100,15131,15163,15194,15224, +15255,15285,15315,15344,15373,15402,15430,15459, +15487,15514,15542,15569,15596,15623,15649,15675, +15701,15727,15753,15778,15803,15828,15853,15877, +15901,15925,15949,15973,15996,16020,16043,16065, +}; + + +// We use binary search to find the right slot +// with at most 7 comparisons. +static unsigned int find_volume(unsigned short vol) +{ + unsigned int l = 0, r = 128; + + while (l < r) { + unsigned int m = l + ((r - l) / 2); + unsigned short p = GMvolTransition[m]; + + if (p < vol) + l = m + 1; + else + r = m; + } + + return l; +} + + +unsigned int get_freq_from_period(int period, int linear) +{ + if (period <= 0) + return INT_MAX; + else if (linear) + return period; + else + return _muldiv(8363, 1712L << 8, (period << 8)); +} + + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// XXX * I prefixed these with `rn_' to avoid any namespace conflicts +// XXX Needs better naming! +// XXX * Keep inline? +// XXX * Get rid of the pointer passing where it is not needed +// + + +static inline void rn_tremor(song_voice_t *chan, int *vol) +{ + if ((chan->cd_tremor & 192) == 128) + *vol = 0; + + chan->flags |= CHN_FASTVOLRAMP; +} + + +static inline int rn_vibrato(song_t *csf, song_voice_t *chan, int period) +{ + unsigned int vibpos = chan->vibrato_position & 0xFF; + int vdelta; + unsigned int vdepth; + + switch (chan->vib_type) { + case VIB_SINE: + default: + vdelta = sine_table[vibpos]; + break; + case VIB_RAMP_DOWN: + vdelta = ramp_down_table[vibpos]; + break; + case VIB_SQUARE: + vdelta = square_table[vibpos]; + break; + case VIB_RANDOM: + vdelta = 128 * ((double) rand() / RAND_MAX) - 64; + break; + } + + if (csf->flags & SONG_ITOLDEFFECTS) { + vdepth = 5; + vdelta = -vdelta; // yes, IT does vibrato backwards in old-effects mode. try it. + } else { + vdepth = 6; + } + vdelta = (vdelta * (int)chan->vibrato_depth) >> vdepth; + + if (csf->flags & SONG_LINEARSLIDES) { + int l = abs(vdelta); + + if (vdelta < 0) { + vdelta = _muldiv(period, linear_slide_up_table[l >> 2], 0x10000) - period; + + if (l & 0x03) + vdelta += _muldiv(period, fine_linear_slide_up_table[l & 0x03], 0x10000) - period; + } else { + vdelta = _muldiv(period, linear_slide_down_table[l >> 2], 0x10000) - period; + + if (l & 0x03) + vdelta += _muldiv(period, fine_linear_slide_down_table[l & 0x03], 0x10000) - period; + } + } + + period -= vdelta; + + // handle on tick-N, or all ticks if not in old-effects mode + if (!(csf->flags & SONG_FIRSTTICK) || !(csf->flags & SONG_ITOLDEFFECTS)) { + chan->vibrato_position = (vibpos + 4 * chan->vibrato_speed) & 0xFF; + } + + return period; +} + +static inline int rn_sample_vibrato(song_voice_t *chan, int period) +{ + unsigned int vibpos = chan->autovib_position & 0xFF; + int vdelta, adepth; + song_sample_t *pins = chan->ptr_sample; + + /* + 1) Mov AX, [SomeVariableNameRelatingToVibrato] + 2) Add AL, Rate + 3) AdC AH, 0 + 4) AH contains the depth of the vibrato as a fine-linear slide. + 5) Mov [SomeVariableNameRelatingToVibrato], AX ; For the next cycle. + */ + + adepth = chan->autovib_depth; // (1) + adepth += pins->vib_rate & 0xff; // (2 & 3) + /* need this cast -- if adepth is unsigned, large autovib will crash the mixer (why? I don't know!) + but if vib_depth is changed to signed, that screws up other parts of the code. ugh. */ + adepth = MIN(adepth, (int) (pins->vib_depth << 8)); + chan->autovib_depth = adepth; // (5) + adepth >>= 8; // (4) + + chan->autovib_position += pins->vib_speed; + + switch(pins->vib_type) { + case VIB_SINE: + default: + vdelta = sine_table[vibpos]; + break; + case VIB_RAMP_DOWN: + vdelta = ramp_down_table[vibpos]; + break; + case VIB_SQUARE: + vdelta = square_table[vibpos]; + break; + case VIB_RANDOM: + vdelta = 128 * ((double) rand() / RAND_MAX) - 64; + break; + } + vdelta = (vdelta * adepth) >> 6; + + int l = abs(vdelta); + if (vdelta < 0) { + vdelta = _muldiv(period, linear_slide_up_table[l >> 2], 0x10000) - period; + + if (l & 0x03) + vdelta += _muldiv(period, fine_linear_slide_up_table[l & 0x03], 0x10000) - period; + } else { + vdelta = _muldiv(period, linear_slide_down_table[l >> 2], 0x10000) - period; + + if (l & 0x03) + vdelta += _muldiv(period, fine_linear_slide_down_table[l & 0x03], 0x10000) - period; + } + + return period - vdelta; +} + + +static inline void rn_process_envelope(song_voice_t *chan, int *nvol) +{ + song_instrument_t *penv = chan->ptr_instrument; + int vol = *nvol; + + // Volume Envelope + if (chan->flags & CHN_VOLENV && penv->vol_env.nodes) { + int envpos = chan->vol_env_position; + unsigned int pt = penv->vol_env.nodes - 1; + + for (unsigned int i = 0; i < (unsigned int)(penv->vol_env.nodes - 1); i++) { + if (envpos <= penv->vol_env.ticks[i]) { + pt = i; + break; + } + } + + int x2 = penv->vol_env.ticks[pt]; + int x1, envvol; + + if (envpos >= x2) { + envvol = penv->vol_env.values[pt] << 2; + x1 = x2; + } else if (pt) { + envvol = penv->vol_env.values[pt-1] << 2; + x1 = penv->vol_env.ticks[pt-1]; + } else { + envvol = 0; + x1 = 0; + } + + if (envpos > x2) + envpos = x2; + + if (x2 > x1 && envpos > x1) { + envvol += ((envpos - x1) * (((int)penv->vol_env.values[pt]<<2) - envvol)) / (x2 - x1); + } + + envvol = CLAMP(envvol, 0, 256); + vol = (vol * envvol) >> 8; + } + + // Panning Envelope + if ((chan->flags & CHN_PANENV) && (penv->pan_env.nodes)) { + int envpos = chan->pan_env_position; + unsigned int pt = penv->pan_env.nodes - 1; + + for (unsigned int i=0; i<(unsigned int)(penv->pan_env.nodes-1); i++) { + if (envpos <= penv->pan_env.ticks[i]) { + pt = i; + break; + } + } + + int x2 = penv->pan_env.ticks[pt], y2 = penv->pan_env.values[pt]; + int x1, envpan; + + if (envpos >= x2) { + envpan = y2; + x1 = x2; + } else if (pt) { + envpan = penv->pan_env.values[pt-1]; + x1 = penv->pan_env.ticks[pt-1]; + } else { + envpan = 128; + x1 = 0; + } + + if (x2 > x1 && envpos > x1) { + envpan += ((envpos - x1) * (y2 - envpan)) / (x2 - x1); + } + + envpan = CLAMP(envpan, 0, 64); + + int pan = chan->final_panning; + + if (pan >= 128) { + pan += ((envpan - 32) * (256 - pan)) / 32; + } else { + pan += ((envpan - 32) * (pan)) / 32; + } + + chan->final_panning = pan; + } + + // FadeOut volume + if (chan->flags & CHN_NOTEFADE) { + unsigned int fadeout = penv->fadeout; + + if (fadeout) { + chan->fadeout_volume -= fadeout << 1; + + if (chan->fadeout_volume <= 0) + chan->fadeout_volume = 0; + + vol = (vol * chan->fadeout_volume) >> 16; + } else if (!chan->fadeout_volume) { + vol = 0; + } + } + + // Pitch/Pan separation + if (penv->pitch_pan_separation && chan->final_panning && chan->note) { + // PPS value is 1/512, i.e. PPS=1 will adjust by 8/512 = 1/64 for each 8 semitones + // with PPS = 32 / PPC = C-5, E-6 will pan hard right (and D#6 will not) + chan->final_panning += ((int) (chan->note - penv->pitch_pan_center - 1) + * penv->pitch_pan_separation) / 4; + } + + *nvol = vol; +} + + +static inline int rn_arpeggio(song_t *csf, song_voice_t *chan, int period) +{ + int a; + + switch ((csf->current_speed - csf->tick_count) % 3) { + case 1: + a = chan->mem_arpeggio >> 4; + break; + case 2: + a = chan->mem_arpeggio & 0xf; + break; + default: + a = 0; + } + + if (!a) + return period; + + a = linear_slide_up_table[a * 16]; + return ((csf->flags & SONG_LINEARSLIDES) + ? _muldiv(period, a, 65536) + : _muldiv(period, 65536, a)); +} + + +static inline void rn_pitch_filter_envelope(song_voice_t *chan, int *nenvpitch, int *nperiod) +{ + song_instrument_t *penv = chan->ptr_instrument; + int envpos = chan->pitch_env_position; + unsigned int pt = penv->pitch_env.nodes - 1; + int period = *nperiod; + int envpitch = *nenvpitch; + + for (unsigned int i = 0; i < (unsigned int)(penv->pitch_env.nodes - 1); i++) { + if (envpos <= penv->pitch_env.ticks[i]) { + pt = i; + break; + } + } + + int x2 = penv->pitch_env.ticks[pt]; + int x1; + + if (envpos >= x2) { + envpitch = (((int)penv->pitch_env.values[pt]) - 32) * 8; + x1 = x2; + } else if (pt) { + envpitch = (((int)penv->pitch_env.values[pt - 1]) - 32) * 8; + x1 = penv->pitch_env.ticks[pt - 1]; + } else { + envpitch = 0; + x1 = 0; + } + + if (envpos > x2) + envpos = x2; + + if (x2 > x1 && envpos > x1) { + int envpitchdest = (((int)penv->pitch_env.values[pt]) - 32) * 8; + envpitch += ((envpos - x1) * (envpitchdest - envpitch)) / (x2 - x1); + } + + // clamp to -255/255? + envpitch = CLAMP(envpitch, -256, 256); + + // Pitch Envelope + if (!(penv->flags & ENV_FILTER)) { + int l = abs(envpitch); + + if (l > 255) + l = 255; + + period = _muldiv(period, (envpitch < 0 ? + linear_slide_down_table : linear_slide_up_table)[l], 0x10000); + } + + *nperiod = period; + *nenvpitch = envpitch; +} + + +static inline void _process_envelope(song_voice_t *chan, song_instrument_t *penv, song_envelope_t *envelope, + int *position, uint32_t env_flag, uint32_t loop_flag, uint32_t sus_flag, + uint32_t fade_flag) +{ + int start = 0, end = 0x7fffffff; + + if (!(chan->flags & env_flag)) { + return; + } + + (*position)++; + + if ((penv->flags & sus_flag) && !(chan->flags & CHN_KEYOFF)) { + start = envelope->ticks[envelope->sustain_start]; + end = envelope->ticks[envelope->sustain_end] + 1; + fade_flag = 0; + } else if (penv->flags & loop_flag) { + start = envelope->ticks[envelope->loop_start]; + end = envelope->ticks[envelope->loop_end] + 1; + fade_flag = 0; + } else { + // End of envelope (?) + start = end = envelope->ticks[envelope->nodes - 1]; + } + if (*position >= end) { + if (fade_flag && !envelope->values[envelope->nodes - 1]) { + chan->fadeout_volume = chan->final_volume = 0; + } + *position = start; + chan->flags |= fade_flag; // only relevant for volume envelope + } +} + +static inline void rn_increment_env_pos(song_voice_t *chan) +{ + song_instrument_t *penv = chan->ptr_instrument; + + _process_envelope(chan, penv, &penv->vol_env, &chan->vol_env_position, + CHN_VOLENV, ENV_VOLLOOP, ENV_VOLSUSTAIN, CHN_NOTEFADE); + _process_envelope(chan, penv, &penv->pan_env, &chan->pan_env_position, + CHN_PANENV, ENV_PANLOOP, ENV_PANSUSTAIN, 0); + _process_envelope(chan, penv, &penv->pitch_env, &chan->pitch_env_position, + CHN_PITCHENV, ENV_PITCHLOOP, ENV_PITCHSUSTAIN, 0); +} + + +static inline int rn_update_sample(song_t *csf, song_voice_t *chan, int nchan, int master_vol) +{ + // Adjusting volumes + if (csf->mix_channels < 2 || (csf->flags & SONG_NOSTEREO)) { + chan->right_volume_new = (chan->final_volume * master_vol) >> 8; + chan->left_volume_new = chan->right_volume_new; + } else if ((chan->flags & CHN_SURROUND) && !(csf->mix_flags & SNDMIX_NOSURROUND)) { + chan->right_volume_new = (chan->final_volume * master_vol) >> 8; + chan->left_volume_new = -chan->right_volume_new; + } else { + int pan = ((int) chan->final_panning) - 128; + pan *= (int) csf->pan_separation; + pan /= 128; + + + pan += 128; + pan = CLAMP(pan, 0, 256); + + if (csf->mix_flags & SNDMIX_REVERSESTEREO) + pan = 256 - pan; + + int realvol = (chan->final_volume * master_vol) >> (8 - 1); + + chan->left_volume_new = (realvol * pan) >> 8; + chan->right_volume_new = (realvol * (256 - pan)) >> 8; + } + + // Clipping volumes + if (chan->right_volume_new > 0xFFFF) + chan->right_volume_new = 0xFFFF; + + if (chan->left_volume_new > 0xFFFF) + chan->left_volume_new = 0xFFFF; + + // Check IDO + if (csf->mix_flags & SNDMIX_NORESAMPLING) { + chan->flags &= ~(CHN_HQSRC); + chan->flags |= CHN_NOIDO; + } else { + chan->flags &= ~(CHN_NOIDO | CHN_HQSRC); + + if (chan->increment == 0x10000) { + chan->flags |= CHN_NOIDO; + } else { + if (!(csf->mix_flags & SNDMIX_HQRESAMPLER) && + !(csf->mix_flags & SNDMIX_ULTRAHQSRCMODE)) { + if (chan->increment >= 0xFF00) + chan->flags |= CHN_NOIDO; + } + } + } + + chan->right_volume_new >>= MIXING_ATTENUATION; + chan->left_volume_new >>= MIXING_ATTENUATION; + chan->right_ramp = + chan->left_ramp = 0; + + // Checking Ping-Pong Loops + if (chan->flags & CHN_PINGPONGFLAG) + chan->increment = -chan->increment; + + if (chan->flags & CHN_MUTE) { + chan->left_volume = chan->right_volume = 0; + } else if (!(csf->mix_flags & SNDMIX_NORAMPING) && + chan->flags & CHN_VOLUMERAMP && + (chan->right_volume != chan->right_volume_new || + chan->left_volume != chan->left_volume_new)) { + // Setting up volume ramp + int ramp_length = volume_ramp_samples; + int right_delta = ((chan->right_volume_new - chan->right_volume) << VOLUMERAMPPRECISION); + int left_delta = ((chan->left_volume_new - chan->left_volume) << VOLUMERAMPPRECISION); + + if (csf->mix_flags & SNDMIX_HQRESAMPLER) { + if (chan->right_volume | chan->left_volume && + chan->right_volume_new | chan->left_volume_new && + !(chan->flags & CHN_FASTVOLRAMP)) { + ramp_length = csf->buffer_count; + + int l = (1 << (VOLUMERAMPPRECISION - 1)); + int r =(int) volume_ramp_samples; + + ramp_length = CLAMP(ramp_length, l, r); + } + } + + chan->right_ramp = right_delta / ramp_length; + chan->left_ramp = left_delta / ramp_length; + chan->right_volume = chan->right_volume_new - ((chan->right_ramp * ramp_length) >> VOLUMERAMPPRECISION); + chan->left_volume = chan->left_volume_new - ((chan->left_ramp * ramp_length) >> VOLUMERAMPPRECISION); + + if (chan->right_ramp | chan->left_ramp) { + chan->ramp_length = ramp_length; + } else { + chan->flags &= ~CHN_VOLUMERAMP; + chan->right_volume = chan->right_volume_new; + chan->left_volume = chan->left_volume_new; + } + } else { + chan->flags &= ~CHN_VOLUMERAMP; + chan->right_volume = chan->right_volume_new; + chan->left_volume = chan->left_volume_new; + } + + chan->right_ramp_volume = chan->right_volume << VOLUMERAMPPRECISION; + chan->left_ramp_volume = chan->left_volume << VOLUMERAMPPRECISION; + + // Adding the channel in the channel list + csf->voice_mix[csf->num_voices++] = nchan; + + if (csf->num_voices >= MAX_VOICES) + return 0; + + return 1; +} + + +// XXX Rename this +static inline void rn_gen_key(song_t *csf, song_voice_t *chan, int chan_num, int freq, int vol) +{ + if (chan->flags & CHN_MUTE) { + // don't do anything + return; + } else if (csf->flags & SONG_INSTRUMENTMODE && + chan->ptr_instrument && + chan->ptr_instrument->midi_channel_mask > 0) { + + // Vol maximum is 64*64 here. (4096) + int volume = vol; + + if ((chan->flags & CHN_ADLIB) && volume > 0) { + // This gives a value in the range 0..127. + //int o = volume; + volume = find_volume((unsigned short) volume) * chan->instrument_volume / 64; + //fprintf(stderr, "%d -> %d[%d]\n", o, volume, chan->instrument_volume); + } else { + // This gives a value in the range 0..127. + volume = volume * chan->instrument_volume / 8192; + } + + } + if (chan->flags & CHN_ADLIB) { + // Scaling is needed to get a frequency that matches with ST3 notes. + // 8363 is st3s middle C sample rate. 261.625 is the Hertz for middle C in a tempered scale (A4 = 440) + //Also, note that to be true to ST3, the frequencies should be quantized, like using the glissando control. + + int oplmilliHertz = (long long int)freq*261625L/8363L; + OPL_HertzTouch(chan_num, oplmilliHertz, chan->flags & CHN_KEYOFF); + + // ST32 ignores global & master volume in adlib mode, guess we should do the same -Bisqwit + OPL_Touch(chan_num, NULL, vol * chan->instrument_volume * 63 / (1 << 20)); + } +} + + +static inline void update_vu_meter(song_voice_t *chan) +{ + // Update VU-Meter (final_volume is 14-bit) + // TODO: missing background channels by doing it this way. + // need to use nMasterCh, add the vu meters for each physical voice, and bit shift. + uint32_t vutmp = chan->final_volume >> (14 - 8); + if (vutmp > 0xFF) vutmp = 0xFF; + if (chan->flags & CHN_ADLIB) { + if (chan->strike>2) { chan->vu_meter=(0xFF*chan->final_volume)>>14;} + // fake VU decay (intentionally similar to ST3) + if (chan->vu_meter > VUMETER_DECAY) { + chan->vu_meter -= VUMETER_DECAY; + } else { + chan->vu_meter = 0; + } + if (chan->vu_meter >= 0x100) { + chan->vu_meter = vutmp; + } + } else if (vutmp && chan->current_sample_data) { + // can't fake the funk + int n; + int pos = chan->position; // necessary on 64-bit systems (sometimes pos == -1, weird) + if (chan->flags & CHN_16BIT) { + const signed short *p = (signed short *)(chan->current_sample_data); + if (chan->flags & CHN_STEREO) + n = p[2 * pos]; + else + n = p[pos]; + n >>= 8; + } else { + const signed char *p = (signed char *)(chan->current_sample_data); + if (chan->flags & CHN_STEREO) + n = p[2 * pos]; + else + n = p[pos]; + } + if (n < 0) + n = -n; + vutmp *= n; + vutmp >>= 7; // 0..255 + chan->vu_meter = vutmp; + } else { + chan->vu_meter = 0; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +int csf_init_player(song_t *csf, int reset) +{ + if (max_voices > MAX_VOICES) + max_voices = MAX_VOICES; + + csf->mix_frequency = CLAMP(csf->mix_frequency, 4000, MAX_SAMPLE_RATE); + volume_ramp_samples = (csf->mix_frequency * VOLUMERAMPLEN) / 100000; + + if (volume_ramp_samples < 8) + volume_ramp_samples = 8; + + if (csf->mix_flags & SNDMIX_NORAMPING) + volume_ramp_samples = 2; + + g_dry_rofs_vol = g_dry_lofs_vol = 0; + + if (reset) { + global_vu_left = 0; + global_vu_right = 0; + } + + initialize_eq(reset, csf->mix_frequency); + + // retarded hackaround to get adlib to suck less + if (csf->mix_frequency != 4000) + Fmdrv_Init(csf->mix_frequency); + OPL_Reset(); + return 1; +} + + +unsigned int csf_read(song_t *csf, void * v_buffer, unsigned int bufsize) +{ + uint8_t * buffer = (uint8_t *)v_buffer; + convert_t convert_func = clip_32_to_8; + int32_t vu_min[2]; + int32_t vu_max[2]; + unsigned int bufleft, max, sample_size, count, smpcount, mix_stat=0; + + vu_min[0] = vu_min[1] = 0x7FFFFFFF; + vu_max[0] = vu_max[1] = -0x7FFFFFFF; + + + csf->mix_stat = 0; + sample_size = csf->mix_channels; + + if (csf->mix_bits_per_sample == 16) { sample_size *= 2; convert_func = clip_32_to_16; } + else if (csf->mix_bits_per_sample == 24) { sample_size *= 3; convert_func = clip_32_to_24; } + else if (csf->mix_bits_per_sample == 32) { sample_size *= 4; convert_func = clip_32_to_32; } + + max = bufsize / sample_size; + + if (!max || !buffer) { + return 0; + } + + bufleft = max; + + if (csf->flags & SONG_ENDREACHED) + bufleft = 0; // skip the loop + + while (bufleft > 0) { + // Update Channel Data + + if (!csf->buffer_count) { + if (!(csf->mix_flags & SNDMIX_DIRECTTODISK)) + csf->buffer_count = bufleft; + + if (!csf_read_note(csf)) { + csf->flags |= SONG_ENDREACHED; + + if (csf->stop_at_order > -1) + return 0; /* faster */ + + if (bufleft == max) + break; + + if (!(csf->mix_flags & SNDMIX_DIRECTTODISK)) + csf->buffer_count = bufleft; + } + + if (!csf->buffer_count) + break; + } + + count = csf->buffer_count; + + if (count > MIXBUFFERSIZE) + count = MIXBUFFERSIZE; + + if (count > bufleft) + count = bufleft; + + if (!count) + break; + + smpcount = count; + + // Resetting sound buffer + stereo_fill(csf->mix_buffer, smpcount, &g_dry_rofs_vol, &g_dry_lofs_vol); + + if (csf->mix_channels >= 2) { + smpcount *= 2; + csf->mix_stat += csf_create_stereo_mix(csf, count); + } else { + csf->mix_stat += csf_create_stereo_mix(csf, count); + mono_from_stereo(csf->mix_buffer, count); + } + + // Handle eq + if (csf->mix_channels >= 2) + eq_stereo(csf, csf->mix_buffer, count); + else + eq_mono(csf, csf->mix_buffer, count); + + mix_stat++; + + if (csf->multi_write) { + /* multi doesn't actually write meaningful data into 'buffer', so we can use that + as temp space for converting */ + for (unsigned int n = 0; n < 64; n++) { + if (csf->multi_write[n].used) { + unsigned int bytes = convert_func(buffer, csf->multi_write[n].buffer, + smpcount, vu_min, vu_max); + csf->multi_write[n].write(csf->multi_write[n].data, buffer, bytes); + } else { + csf->multi_write[n].silence(csf->multi_write[n].data, + smpcount * ((csf->mix_bits_per_sample + 7) / 8)); + } + } + } else { + // Perform clipping + VU-Meter + buffer += convert_func(buffer, csf->mix_buffer, smpcount, vu_min, vu_max); + } + + // Buffer ready + bufleft -= count; + csf->buffer_count -= count; + } + + if (bufleft) + memset(buffer, (csf->mix_bits_per_sample == 8) ? 0x80 : 0, bufleft * sample_size); + + // VU-Meter + //Reduce range to 8bits signed (-128 to 127). + vu_min[0] >>= 19; + vu_min[1] >>= 19; + vu_max[0] >>= 19; + vu_max[1] >>= 19; + + if (vu_max[0] < vu_min[0]) + vu_max[0] = vu_min[0]; + + if (vu_max[1] < vu_min[1]) + vu_max[1] = vu_min[1]; + + global_vu_left = (unsigned int)(vu_max[0] - vu_min[0]); + + global_vu_right = (unsigned int)(vu_max[1] - vu_min[1]); + + if (mix_stat) { + csf->mix_stat += mix_stat - 1; + csf->mix_stat /= mix_stat; + } + + return max - bufleft; +} + + + +///////////////////////////////////////////////////////////////////////////// +// Handles navigation/effects + +static int increment_order(song_t *csf) +{ + csf->process_row = csf->break_row; /* [ProcessRow = BreakRow] */ + csf->break_row = 0; /* [BreakRow = 0] */ + + /* some ugly copypasta, this should be less dumb */ + if (csf->flags & SONG_PATTERNPLAYBACK) { + /* process_order is hijacked as a "playback initiated" flag -- otherwise repeat count + would be incremented as soon as pattern playback started. (this is a stupid hack) */ + if (csf->process_order) { + if (++csf->repeat_count) { + if (UNLIKELY(csf->repeat_count < 0)) { + csf->repeat_count = 1; // it overflowed! + } + } else { + csf->process_row = PROCESS_NEXT_ORDER; + return 0; + } + } else { + csf->process_order = 1; + } + } else if (!(csf->flags & SONG_ORDERLOCKED)) { + /* [Increase ProcessOrder] */ + /* [while Order[ProcessOrder] = 0xFEh, increase ProcessOrder] */ + do { + csf->process_order++; + } while (csf->orderlist[csf->process_order] == ORDER_SKIP); + + /* [if Order[ProcessOrder] = 0xFFh, ProcessOrder = 0] (... or just stop playing) */ + if (csf->orderlist[csf->process_order] == ORDER_LAST) { + if (++csf->repeat_count) { + if (UNLIKELY(csf->repeat_count < 0)) { + csf->repeat_count = 1; // it overflowed! + } + } else { + csf->process_row = PROCESS_NEXT_ORDER; + return 0; + } + + csf->process_order = 0; + while (csf->orderlist[csf->process_order] == ORDER_SKIP) + csf->process_order++; + } + if (csf->orderlist[csf->process_order] >= MAX_PATTERNS) { + // what the butt? + csf->process_row = PROCESS_NEXT_ORDER; + return 0; + } + + /* [CurrentPattern = Order[ProcessOrder]] */ + csf->current_order = csf->process_order; + csf->current_pattern = csf->orderlist[csf->process_order]; + } + + if (!csf->pattern_size[csf->current_pattern] || !csf->patterns[csf->current_pattern]) { + /* okay, this is wrong. allocate the pattern _NOW_ */ + csf->patterns[csf->current_pattern] = csf_allocate_pattern(64); + csf->pattern_size[csf->current_pattern] = 64; + csf->pattern_alloc_size[csf->current_pattern] = 64; + } + + if (csf->process_row >= csf->pattern_size[csf->current_pattern]) { + // Cxx to row beyond end of pattern: use 0 instead + csf->process_row = 0; + } + + return 1; +} + + +int csf_process_tick(song_t *csf) +{ + csf->flags &= ~SONG_FIRSTTICK; + /* [Decrease tick counter. Is tick counter 0?] */ + if (--csf->tick_count == 0) { + /* [-- Yes --] */ + + /* [Tick counter = Tick counter set (the current 'speed')] */ + csf->tick_count = csf->current_speed; + + /* [Decrease row counter. Is row counter 0?] */ + if (--csf->row_count <= 0) { + /* [-- Yes --] */ + + /* [Row counter = 1] + this uses zero, in order to simplify SEx effect handling -- SEx has no effect if a + channel to its left has already set the delay value. thus we set the row counter + there to (value + 1) which is never zero, but 0 and 1 are fundamentally equivalent + as far as csf_process_tick is concerned. */ + csf->row_count = 0; + + /* [Increase ProcessRow. Is ProcessRow > NumberOfRows?] */ + if (++csf->process_row >= csf->pattern_size[csf->current_pattern]) { + /* [-- Yes --] */ + + if (!increment_order(csf)) + return 0; + } /* else [-- No --] */ + + /* [CurrentRow = ProcessRow] */ + csf->row = csf->process_row; + + /* [Update Pattern Variables] + (this is handled along with update effects) */ + csf->flags |= SONG_FIRSTTICK; + } else { + /* [-- No --] */ + /* Call update-effects for each channel. */ + } + + + // Reset channel values + song_voice_t *chan = csf->voices; + song_note_t *m = csf->patterns[csf->current_pattern] + csf->row * MAX_CHANNELS; + + for (unsigned int nchan=0; nchanrow_note = m->note; + + if (m->instrument) + chan->last_instrument = m->instrument; + + chan->row_instr = m->instrument; + chan->row_voleffect = m->voleffect; + chan->row_volparam = m->volparam; + chan->row_effect = m->effect; + chan->row_param = m->param; + + chan->left_volume = chan->left_volume_new; + chan->right_volume = chan->right_volume_new; + chan->flags &= ~(CHN_PORTAMENTO | CHN_VIBRATO | CHN_TREMOLO); + chan->n_command = 0; + } + + csf_process_effects(csf, 1); + } else { + /* [-- No --] */ + /* [Update effects for each channel as required.] */ + + if (csf_midi_out_note) { + song_note_t *m = csf->patterns[csf->current_pattern] + csf->row * MAX_CHANNELS; + + for (unsigned int nchan=0; nchanflags & SONG_PAUSED) { + if (!csf->current_speed) + csf->current_speed = csf->initial_speed ?: 6; + if (!csf->current_tempo) + csf->current_tempo = csf->initial_tempo ?: 125; + + csf->flags &= ~SONG_FIRSTTICK; + + if (--csf->tick_count == 0) { + csf->tick_count = csf->current_speed; + if (--csf->row_count <= 0) { + csf->row_count = 0; + //csf->flags |= SONG_FIRSTTICK; + } + // clear channel values (similar to csf_process_tick) + for (cn = 0, chan = csf->voices; cn < MAX_CHANNELS; cn++, chan++) { + chan->row_note = 0; + chan->row_instr = 0; + chan->row_voleffect = 0; + chan->row_volparam = 0; + chan->row_effect = 0; + chan->row_param = 0; + chan->n_command = 0; + } + } + csf_process_effects(csf, 0); + } else { + if (!csf_process_tick(csf)) + return 0; + } + + //////////////////////////////////////////////////////////////////////////////////// + + if (!csf->current_tempo) + return 0; + + csf->buffer_count = (csf->mix_frequency * 5 * csf->tempo_factor) / (csf->current_tempo << 8); + + // chaseback hoo hah + if (csf->stop_at_order > -1 && csf->stop_at_row > -1) { + if (csf->stop_at_order <= (signed) csf->current_order && + csf->stop_at_row <= (signed) csf->row) { + return 0; + } + } + + //////////////////////////////////////////////////////////////////////////////////// + // Update channels data + + // Master Volume + Pre-Amplification / Attenuation setup + uint32_t master_vol = csf->mixing_volume << 2; // yields maximum of 0x200 + + csf->num_voices = 0; + + for (cn = 0, chan = csf->voices; cn < MAX_VOICES; cn++, chan++) { + /*if(cn == 0 || cn == 1) + fprintf(stderr, "considering channel %d (per %d, pos %d/%d, flags %X)\n", + (int)cn, chan->period, chan->position, chan->length, chan->flags);*/ + + if (chan->flags & CHN_NOTEFADE && + !(chan->fadeout_volume | chan->right_volume | chan->left_volume)) { + chan->length = 0; + chan->rofs = + chan->lofs = 0; + continue; + } + + // Check for unused channel + if (cn >= MAX_CHANNELS && !chan->length) { + continue; + } + + // Reset channel data + chan->increment = 0; + chan->final_volume = 0; + chan->final_panning = chan->panning + chan->pan_swing + chan->panbrello_delta; + chan->ramp_length = 0; + + // Calc Frequency + if (chan->period && (chan->length || (chan->flags & CHN_ADLIB))) { + int vol = chan->volume; + + if (chan->flags & CHN_TREMOLO) + vol += chan->tremolo_delta; + + vol = CLAMP(vol, 0, 256); + + // Tremor + if (chan->n_command == FX_TREMOR) + rn_tremor(chan, &vol); + + // Clip volume + vol = CLAMP(vol, 0, 0x100); + vol <<= 6; + + // Process Envelopes + if ((csf->flags & SONG_INSTRUMENTMODE) && chan->ptr_instrument) { + rn_process_envelope(chan, &vol); + } else { + // No Envelope: key off => note cut + // 1.41-: CHN_KEYOFF|CHN_NOTEFADE + if (chan->flags & CHN_NOTEFADE) { + chan->fadeout_volume = 0; + vol = 0; + } + } + + // vol is 14-bits + if (vol) { + // IMPORTANT: chan->final_volume is 14 bits !!! + // -> _muldiv( 14+7, 6+6, 18); => RealVolume: 14-bit result (21+12-19) + chan->final_volume = _muldiv + (vol * csf->current_global_volume, + chan->global_volume + * CLAMP(chan->instrument_volume + chan->vol_swing, 0, 64), + 1 << 19); + } + + int period = chan->period; + + if ((chan->flags & (CHN_GLISSANDO|CHN_PORTAMENTO)) == (CHN_GLISSANDO|CHN_PORTAMENTO)) { + period = get_period_from_note(get_note_from_period(period), + chan->c5speed, csf->flags & SONG_LINEARSLIDES); + } + + // Arpeggio ? + if (chan->n_command == FX_ARPEGGIO) + period = rn_arpeggio(csf, chan, period); + + // Pitch/Filter Envelope + int envpitch = 0; + + if ((csf->flags & SONG_INSTRUMENTMODE) && chan->ptr_instrument + && (chan->flags & CHN_PITCHENV) && chan->ptr_instrument->pitch_env.nodes) + rn_pitch_filter_envelope(chan, &envpitch, &period); + + // Vibrato + if (chan->flags & CHN_VIBRATO) + period = rn_vibrato(csf, chan, period); + + // Sample Auto-Vibrato + if (chan->ptr_sample && chan->ptr_sample->vib_depth) { + period = rn_sample_vibrato(chan, period); + } + + unsigned int freq = get_freq_from_period(period, csf->flags & SONG_LINEARSLIDES); + + if (!(chan->flags & CHN_NOTEFADE)) + rn_gen_key(csf, chan, cn, freq, vol); + + // Filter Envelope: controls cutoff frequency + if (chan && chan->ptr_instrument && chan->ptr_instrument->flags & ENV_FILTER) { + setup_channel_filter(chan, + !(chan->flags & CHN_FILTER), envpitch, csf->mix_frequency); + } + + chan->sample_freq = freq; + + unsigned int ninc = _muldiv(freq, 0x10000, csf->mix_frequency); + + if (ninc >= 0xFFB0 && ninc <= 0x10090) + ninc = 0x10000; + + if (csf->freq_factor != 128) + ninc = (ninc * csf->freq_factor) >> 7; + + if (ninc > 0xFF0000) + ninc = 0xFF0000; + + chan->increment = (ninc + 1) & ~3; + } + + // Increment envelope position + if (csf->flags & SONG_INSTRUMENTMODE && chan->ptr_instrument) + rn_increment_env_pos(chan); + + chan->final_panning = CLAMP(chan->final_panning, 0, 256); + + // Volume ramping + chan->flags &= ~CHN_VOLUMERAMP; + + if (chan->final_volume || chan->left_volume || chan->right_volume) + chan->flags |= CHN_VOLUMERAMP; + + if (chan->strike) + chan->strike--; + + // Check for too big increment + if (((chan->increment >> 16) + 1) >= (int)(chan->loop_end - chan->loop_start)) + chan->flags &= ~CHN_LOOP; + + chan->right_volume_new = chan->left_volume_new = 0; + if (!(chan->length && chan->increment)) + chan->current_sample_data = NULL; + + update_vu_meter(chan); + + if (chan->current_sample_data) { + if (!rn_update_sample(csf, chan, cn, master_vol)) + break; + } else { + // Note change but no sample + //if (chan->vu_meter > 0xFF) chan->vu_meter = 0; + chan->left_volume = chan->right_volume = 0; + chan->length = 0; + } + } + + // Checking Max Mix Channels reached: ordering by volume + if (csf->num_voices >= max_voices && (!(csf->mix_flags & SNDMIX_DIRECTTODISK))) { + for (unsigned int i = 0; i < csf->num_voices; i++) { + unsigned int j = i; + + while ((j + 1 < csf->num_voices) && + (csf->voices[csf->voice_mix[j]].final_volume + < csf->voices[csf->voice_mix[j + 1]].final_volume)) + { + unsigned int n = csf->voice_mix[j]; + csf->voice_mix[j] = csf->voice_mix[j + 1]; + csf->voice_mix[j + 1] = n; + j++; + } + } + } + + return 1; +} + -- cgit v1.2.3