Mercurial > SDL_sound_CoreAudio
diff decoders/timidity/mix.c @ 199:2d887640d300
Initial add.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Fri, 04 Jan 2002 06:49:49 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/decoders/timidity/mix.c Fri Jan 04 06:49:49 2002 +0000 @@ -0,0 +1,573 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> + + Suddenly, you realize that this program is free software; you get + an overwhelming urge to 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 another copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + I bet they'll be amazed. + + mix.c */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +#include "SDL_sound.h" + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#include "timidity.h" +#include "options.h" +#include "instrum.h" +#include "playmidi.h" +#include "output.h" +#include "tables.h" +#include "resample.h" +#include "mix.h" + +/* Returns 1 if envelope runs out */ +int recompute_envelope(MidiSong *song, int v) +{ + int stage; + + stage = song->voice[v].envelope_stage; + + if (stage>5) + { + /* Envelope ran out. */ + song->voice[v].status = VOICE_FREE; + return 1; + } + + if (song->voice[v].sample->modes & MODES_ENVELOPE) + { + if (song->voice[v].status==VOICE_ON || song->voice[v].status==VOICE_SUSTAINED) + { + if (stage>2) + { + /* Freeze envelope until note turns off. Trumpets want this. */ + song->voice[v].envelope_increment=0; + return 0; + } + } + } + song->voice[v].envelope_stage=stage+1; + + if (song->voice[v].envelope_volume==song->voice[v].sample->envelope_offset[stage]) + return recompute_envelope(song, v); + song->voice[v].envelope_target = song->voice[v].sample->envelope_offset[stage]; + song->voice[v].envelope_increment = song->voice[v].sample->envelope_rate[stage]; + if (song->voice[v].envelope_target < song->voice[v].envelope_volume) + song->voice[v].envelope_increment = -song->voice[v].envelope_increment; + return 0; +} + +void apply_envelope_to_amp(MidiSong *song, int v) +{ + float lamp = song->voice[v].left_amp, ramp; + Sint32 la,ra; + if (song->voice[v].panned == PANNED_MYSTERY) + { + ramp = song->voice[v].right_amp; + if (song->voice[v].tremolo_phase_increment) + { + lamp *= song->voice[v].tremolo_volume; + ramp *= song->voice[v].tremolo_volume; + } + if (song->voice[v].sample->modes & MODES_ENVELOPE) + { + lamp *= (float)vol_table[song->voice[v].envelope_volume>>23]; + ramp *= (float)vol_table[song->voice[v].envelope_volume>>23]; + } + + la = (Sint32)FSCALE(lamp,AMP_BITS); + + if (la>MAX_AMP_VALUE) + la=MAX_AMP_VALUE; + + ra = (Sint32)FSCALE(ramp,AMP_BITS); + if (ra>MAX_AMP_VALUE) + ra=MAX_AMP_VALUE; + + song->voice[v].left_mix = la; + song->voice[v].right_mix = ra; + } + else + { + if (song->voice[v].tremolo_phase_increment) + lamp *= song->voice[v].tremolo_volume; + if (song->voice[v].sample->modes & MODES_ENVELOPE) + lamp *= (float)vol_table[song->voice[v].envelope_volume>>23]; + + la = (Sint32)FSCALE(lamp,AMP_BITS); + + if (la>MAX_AMP_VALUE) + la=MAX_AMP_VALUE; + + song->voice[v].left_mix = la; + } +} + +static int update_envelope(MidiSong *song, int v) +{ + song->voice[v].envelope_volume += song->voice[v].envelope_increment; + /* Why is there no ^^ operator?? */ + if (((song->voice[v].envelope_increment < 0) && + (song->voice[v].envelope_volume <= song->voice[v].envelope_target)) || + ((song->voice[v].envelope_increment > 0) && + (song->voice[v].envelope_volume >= song->voice[v].envelope_target))) + { + song->voice[v].envelope_volume = song->voice[v].envelope_target; + if (recompute_envelope(song, v)) + return 1; + } + return 0; +} + +static void update_tremolo(MidiSong *song, int v) +{ + Sint32 depth = song->voice[v].sample->tremolo_depth << 7; + + if (song->voice[v].tremolo_sweep) + { + /* Update sweep position */ + + song->voice[v].tremolo_sweep_position += song->voice[v].tremolo_sweep; + if (song->voice[v].tremolo_sweep_position >= (1 << SWEEP_SHIFT)) + song->voice[v].tremolo_sweep=0; /* Swept to max amplitude */ + else + { + /* Need to adjust depth */ + depth *= song->voice[v].tremolo_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + + song->voice[v].tremolo_phase += song->voice[v].tremolo_phase_increment; + + /* if (song->voice[v].tremolo_phase >= (SINE_CYCLE_LENGTH<<RATE_SHIFT)) + song->voice[v].tremolo_phase -= SINE_CYCLE_LENGTH<<RATE_SHIFT; */ + + song->voice[v].tremolo_volume = (float) + (1.0 - FSCALENEG((sine(song->voice[v].tremolo_phase >> RATE_SHIFT) + 1.0) + * depth * TREMOLO_AMPLITUDE_TUNING, + 17)); + + /* I'm not sure about the +1.0 there -- it makes tremoloed voices' + volumes on average the lower the higher the tremolo amplitude. */ +} + +/* Returns 1 if the note died */ +static int update_signal(MidiSong *song, int v) +{ + if (song->voice[v].envelope_increment && update_envelope(song, v)) + return 1; + + if (song->voice[v].tremolo_phase_increment) + update_tremolo(song, v); + + apply_envelope_to_amp(song, v); + return 0; +} + +#define MIXATION(a) *lp++ += (a)*s; + +static void mix_mystery_signal(MidiSong *song, sample_t *sp, Sint32 *lp, int v, + int count) +{ + Voice *vp = song->voice + v; + final_volume_t + left=vp->left_mix, + right=vp->right_mix; + int cc; + sample_t s; + + if (!(cc = vp->control_counter)) + { + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + right = vp->right_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + right = vp->right_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + return; + } +} + +static void mix_center_signal(MidiSong *song, sample_t *sp, Sint32 *lp, int v, + int count) +{ + Voice *vp = song->voice + v; + final_volume_t + left=vp->left_mix; + int cc; + sample_t s; + + if (!(cc = vp->control_counter)) + { + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + MIXATION(left); + } + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(left); + } + return; + } +} + +static void mix_single_signal(MidiSong *song, sample_t *sp, Sint32 *lp, int v, + int count) +{ + Voice *vp = song->voice + v; + final_volume_t + left=vp->left_mix; + int cc; + sample_t s; + + if (!(cc = vp->control_counter)) + { + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + lp++; + } + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + lp++; + } + return; + } +} + +static void mix_mono_signal(MidiSong *song, sample_t *sp, Sint32 *lp, int v, + int count) +{ + Voice *vp = song->voice + v; + final_volume_t + left=vp->left_mix; + int cc; + sample_t s; + + if (!(cc = vp->control_counter)) + { + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + } + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + } + return; + } +} + +static void mix_mystery(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) +{ + final_volume_t + left = song->voice[v].left_mix, + right = song->voice[v].right_mix; + sample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(right); + } +} + +static void mix_center(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) +{ + final_volume_t + left = song->voice[v].left_mix; + sample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(left); + } +} + +static void mix_single(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) +{ + final_volume_t + left = song->voice[v].left_mix; + sample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + lp++; + } +} + +static void mix_mono(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) +{ + final_volume_t + left = song->voice[v].left_mix; + sample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + } +} + +/* Ramp a note out in c samples */ +static void ramp_out(MidiSong *song, sample_t *sp, Sint32 *lp, int v, Sint32 c) +{ + + /* should be final_volume_t, but Uint8 gives trouble. */ + Sint32 left, right, li, ri; + + sample_t s=0; /* silly warning about uninitialized s */ + + /* Fix by James Caldwell */ + if ( c == 0 ) c = 1; + + left=song->voice[v].left_mix; + li=-(left/c); + if (!li) li=-1; + + /* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */ + + if (!(song->encoding & PE_MONO)) + { + if (song->voice[v].panned==PANNED_MYSTERY) + { + right=song->voice[v].right_mix; + ri=-(right/c); + while (c--) + { + left += li; + if (left<0) + left=0; + right += ri; + if (right<0) + right=0; + s=*sp++; + MIXATION(left); + MIXATION(right); + } + } + else if (song->voice[v].panned==PANNED_CENTER) + { + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + MIXATION(left); + MIXATION(left); + } + } + else if (song->voice[v].panned==PANNED_LEFT) + { + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + MIXATION(left); + lp++; + } + } + else if (song->voice[v].panned==PANNED_RIGHT) + { + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + lp++; + MIXATION(left); + } + } + } + else + { + /* Mono output. */ + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + MIXATION(left); + } + } +} + + +/**************** interface function ******************/ + +void mix_voice(MidiSong *song, Sint32 *buf, int v, Sint32 c) +{ + Voice *vp = song->voice + v; + sample_t *sp; + if (vp->status==VOICE_DIE) + { + if (c>=MAX_DIE_TIME) + c=MAX_DIE_TIME; + sp=resample_voice(song, v, &c); + ramp_out(song, sp, buf, v, c); + vp->status=VOICE_FREE; + } + else + { + sp=resample_voice(song, v, &c); + if (song->encoding & PE_MONO) + { + /* Mono output. */ + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_mono_signal(song, sp, buf, v, c); + else + mix_mono(song, sp, buf, v, c); + } + else + { + if (vp->panned == PANNED_MYSTERY) + { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_mystery_signal(song, sp, buf, v, c); + else + mix_mystery(song, sp, buf, v, c); + } + else if (vp->panned == PANNED_CENTER) + { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_center_signal(song, sp, buf, v, c); + else + mix_center(song, sp, buf, v, c); + } + else + { + /* It's either full left or full right. In either case, + every other sample is 0. Just get the offset right: */ + if (vp->panned == PANNED_RIGHT) buf++; + + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_single_signal(song, sp, buf, v, c); + else + mix_single(song, sp, buf, v, c); + } + } + } +}