Commit 6ea399c1 authored by Erik Faye-Lund's avatar Erik Faye-Lund
Browse files

fixed envelopes with sustain, added eastwing.xm, and added a dos-script to...

fixed envelopes with sustain, added eastwing.xm, and added a dos-script to rebuild the gbfs-stuff. the dos-build process needs a cleanup.

git-svn-id: https://pimpmobile.svn.sourceforge.net/svnroot/pimpmobile/trunk@116 3d5ecaf0-f903-0410-b953-c2c1a4d75763
parent 8d7fef0e
......@@ -32,11 +32,12 @@ OBJS = \
libgbfs.o
MODULES = \
eye.mod \
rhino.mod \
melkedemo2.mod \
hypermmx.xm \
../testbench/env_sustain.xm
# eye.mod \
# rhino.mod \
# melkedemo2.mod \
# hypermmx.xm \
# eastwing.xm \
BIN_MODULES = $(MODULES:=.bin)
......
converter eye.mod
gbfs data.gbfs sample_bank.bin eye.mod.bin
copy /B example.bin+data.gbfs example.gba
\ No newline at end of file
......@@ -4,11 +4,11 @@
#define CYCLES_PR_FRAME 280896
/* 32 is the maximum amount of channels in fasttracker2. a nice default. */
#define CHANNELS 8
#define CHANNELS 16
/* check the sample-rate calculator at http://www.pineight.com/gba/samplerates/ for more glitch-free samplerates */
/* 0x4000100 = 0xFFFF, 0x4000102 = 0x0083 */
#define SAMPLERATE (18157.16)
#define SAMPLERATE (18157.16 * 2)
/* only 130 bytes big, quite damn pleasing results */
#define AMIGA_DELTA_LUT_LOG_SIZE 7
......
......@@ -3,6 +3,22 @@
#include <stdio.h>
#if 0
int __pimp_envelope_find_node(const pimp_envelope *env, int tick)
{
ASSERT(NULL != env);
for (int i = 1; i < env->node_count - 1; ++i)
{
if (env->node_tick[i] <= tick)
{
return i - 1;
}
}
return 0;
}
#endif
#if 0
unsigned eval_vol_env(pimp_channel_state &chan)
{
......@@ -62,10 +78,11 @@ int __pimp_envelope_sample(pimp_envelope_state *state)
{
ASSERT(NULL != state);
// the magnitude of the envelope at tick N:
// first, find the last node at or before tick N - its position is M
// then, the magnitude of the envelope at tick N is given by
// magnitude = node_magnitude[M] + ((node_delta[M] * (N-M)) >> 8)
/* the magnitude of the envelope at tick N:
* first, find the last node at or before tick N - its position is M
* then, the magnitude of the envelope at tick N is given by
* magnitude = node_magnitude[M] + ((node_delta[M] * (N-M)) >> 8)
*/
s32 delta = state->env->node_delta[state->current_node];
u32 internal_tick = state->current_tick - state->env->node_tick[state->current_node];
......@@ -80,20 +97,24 @@ void __pimp_envelope_advance_tick(pimp_envelope_state *state, bool sustain)
{
ASSERT(NULL != state);
// advance a tick (TODO: move to separate function)
/* advance a tick */
state->current_tick++;
iprintf("%d\n", state->current_tick);
if (state->current_node < (state->env->node_count - 1))
/* check for sustain loop */
if ((state->env->flags & (1 << 1)) && (sustain == true))
{
// TODO: sustain
#if 0
if (chan.vol_env->flags & (1 << 1))
if (state->current_tick >= state->env->sustain_loop_end)
{
/* sustain loop */
chan.vol_env_tick = 0; // HACK: just used to make the BP06 demo tune play correctly
state->current_node = state->env->sustain_loop_start;
state->current_tick = state->env->node_tick[state->current_node];
}
else
#endif
}
/* check if we have passed the current node
* we don't need to clamp the envelope-pos to the end, since the last delta is zero
*/
if (state->current_node < (state->env->node_count - 1))
{
if (state->current_tick >= state->env->node_tick[state->current_node + 1])
{
state->current_node++;
......
......@@ -9,6 +9,8 @@ STATIC INLINE void *get_ptr(const unsigned int *offset)
return (char*)offset + *offset;
}
#define KEY_OFF 121
typedef struct
{
/* offset relative to sample-bank */
......
......@@ -17,6 +17,7 @@ typedef struct
// const pimp_envelope *vol_env;
pimp_envelope_state vol_env;
bool sustain;
s32 period;
s32 final_period;
......@@ -31,7 +32,6 @@ typedef struct
u8 effect;
u8 effect_param;
u8 volume_command;
u8 note_off;
// u32 vol_env_tick;
// s8 vol_env_node;
......
......@@ -10,29 +10,29 @@
// #define PRINT_PATTERNS
/* need to move these to a separate channel state header (?) */
STATIC void porta_up(pimp_channel_state &chan, s32 period_low_clamp)
STATIC void porta_up(pimp_channel_state *chan, s32 period_low_clamp)
{
chan.final_period -= chan.porta_speed;
if (chan.final_period < period_low_clamp) chan.final_period = period_low_clamp;
chan->final_period -= chan->porta_speed;
if (chan->final_period < period_low_clamp) chan->final_period = period_low_clamp;
}
STATIC void porta_down(pimp_channel_state &chan, s32 period_high_clamp)
STATIC void porta_down(pimp_channel_state *chan, s32 period_high_clamp)
{
chan.final_period += chan.porta_speed;
if (chan.final_period > period_high_clamp) chan.final_period = period_high_clamp;
chan->final_period += chan->porta_speed;
if (chan->final_period > period_high_clamp) chan->final_period = period_high_clamp;
}
STATIC void porta_note(pimp_channel_state &chan)
STATIC void porta_note(pimp_channel_state *chan)
{
if (chan.final_period > chan.porta_target)
if (chan->final_period > chan->porta_target)
{
chan.final_period -= chan.porta_speed;
if (chan.final_period < chan.porta_target) chan.final_period = chan.porta_target;
chan->final_period -= chan->porta_speed;
if (chan->final_period < chan->porta_target) chan->final_period = chan->porta_target;
}
else if (chan.final_period < chan.porta_target)
else if (chan->final_period < chan->porta_target)
{
chan.final_period += chan.porta_speed;
if (chan.final_period > chan.porta_target) chan.final_period = chan.porta_target;
chan->final_period += chan->porta_speed;
if (chan->final_period > chan->porta_target) chan->final_period = chan->porta_target;
}
}
......@@ -43,18 +43,24 @@ STATIC int __pimp_channel_get_volume(pimp_channel_state *chan)
if (chan->vol_env.env != 0)
{
volume = (volume * __pimp_envelope_sample(&chan->vol_env)) >> 8;
__pimp_envelope_advance_tick(&chan->vol_env, true);
__pimp_envelope_advance_tick(&chan->vol_env, chan->sustain);
}
else
{
if (!chan->sustain) volume = 0;
}
// if (chan->sustain == true) printf("%d\n", volume);
return volume;
}
void update_row(pimp_mod_context *ctx)
STATIC void __pimp_mod_context_update_row(pimp_mod_context *ctx)
{
ASSERT(mod != 0);
for (u32 c = 0; c < ctx->mod->channel_count; ++c)
{
pimp_channel_state &chan = ctx->channels[c];
pimp_channel_state *chan = &ctx->channels[c];
volatile pimp_mixer_channel_state &mc = ctx->mixer->channels[c];
const pimp_pattern_entry *note = &get_pattern_data(ctx->curr_pattern)[ctx->curr_row * ctx->mod->channel_count + c];
......@@ -63,65 +69,77 @@ void update_row(pimp_mod_context *ctx)
print_pattern_entry(*note);
#endif
chan.effect = note->effect_byte;
chan.effect_param = note->effect_parameter;
chan.volume_command = note->volume_command;
chan->effect = note->effect_byte;
chan->effect_param = note->effect_parameter;
chan->volume_command = note->volume_command;
bool period_dirty = false;
bool volume_dirty = false;
if (note->instrument > 0)
if (note->note == KEY_OFF)
{
chan.instrument = __pimp_module_get_instrument(ctx->mod, note->instrument - 1);
chan.sample = get_sample(chan.instrument, chan.instrument->sample_map[note->note]);
chan.vol_env.env = get_vol_env(chan.instrument);
__pimp_envelope_reset(&chan.vol_env);
chan.volume = chan.sample->volume;
volume_dirty = true;
chan->sustain = false;
volume_dirty = true; // we need to update volume if note off killed note
}
if (
chan.instrument != 0 &&
note->note > 0 &&
chan.effect != EFF_PORTA_NOTE &&
chan.effect != EFF_PORTA_NOTE_VOLUME_SLIDE &&
!(chan.effect == EFF_MULTI_FX && chan.effect_param == EFF_NOTE_DELAY))
else
{
if (chan.instrument->sample_count == 0)
if (note->instrument > 0)
{
// stupid musician, tried to play an empty instrument...
mc.sample_data = NULL;
mc.sample_cursor = 0;
mc.sample_cursor_delta = 0;
chan->instrument = __pimp_module_get_instrument(ctx->mod, note->instrument - 1);
chan->sample = get_sample(chan->instrument, chan->instrument->sample_map[note->note]);
chan->vol_env.env = get_vol_env(chan->instrument);
__pimp_envelope_reset(&chan->vol_env);
chan->sustain = true;
chan->volume = chan->sample->volume;
volume_dirty = true;
}
else
if (
chan->instrument != 0 &&
note->note > 0 &&
chan->effect != EFF_PORTA_NOTE &&
chan->effect != EFF_PORTA_NOTE_VOLUME_SLIDE &&
!(chan->effect == EFF_MULTI_FX && chan->effect_param == EFF_NOTE_DELAY))
{
chan.sample = get_sample(chan.instrument, chan.instrument->sample_map[note->note]);
mc.sample_cursor = 0;
mc.sample_data = ctx->sample_bank + chan.sample->data_ptr;
mc.sample_length = chan.sample->length;
mc.loop_type = (pimp_mixer_loop_type)chan.sample->loop_type;
mc.loop_start = chan.sample->loop_start;
mc.loop_end = chan.sample->loop_start + chan.sample->loop_length;
if (ctx->mod->flags & FLAG_LINEAR_PERIODS)
if (chan->instrument->sample_count == 0)
{
chan.period = __pimp_get_linear_period(((s32)note->note) + chan.sample->rel_note, chan.sample->fine_tune);
// stupid musician, tried to play an empty instrument...
mc.sample_data = NULL;
mc.sample_cursor = 0;
mc.sample_cursor_delta = 0;
}
else
{
chan.period = __pimp_get_amiga_period(((s32)note->note) + chan.sample->rel_note, chan.sample->fine_tune);
chan->sample = get_sample(chan->instrument, chan->instrument->sample_map[note->note]);
mc.sample_cursor = 0;
mc.sample_data = ctx->sample_bank + chan->sample->data_ptr;
mc.sample_length = chan->sample->length;
mc.loop_type = (pimp_mixer_loop_type)chan->sample->loop_type;
mc.loop_start = chan->sample->loop_start;
mc.loop_end = chan->sample->loop_start + chan->sample->loop_length;
if (ctx->mod->flags & FLAG_LINEAR_PERIODS)
{
chan->period = __pimp_get_linear_period(((s32)note->note) + chan->sample->rel_note, chan->sample->fine_tune);
}
else
{
chan->period = __pimp_get_amiga_period(((s32)note->note) + chan->sample->rel_note, chan->sample->fine_tune);
}
chan->final_period = chan->period;
chan->sustain = true;
period_dirty = true;
}
chan.final_period = chan.period;
period_dirty = true;
}
}
if (note->instrument > 0)
{
chan.volume = chan.sample->volume;
chan->volume = chan->sample->volume;
volume_dirty = true;
}
......@@ -157,8 +175,8 @@ $f0-$ff Tone porta
}
else
{
chan.volume = note->volume_command - 0x10;
DEBUG_PRINT(("setting volume to: %02X\n", chan.volume));
chan->volume = note->volume_command - 0x10;
DEBUG_PRINT(("setting volume to: %02X\n", chan->volume));
volume_dirty = true;
}
break;
......@@ -168,30 +186,30 @@ $f0-$ff Tone porta
DEBUG_PRINT(("unsupported volume-command %02X\n", note->volume_command));
}
switch (chan.effect)
switch (chan->effect)
{
case EFF_NONE: break;
case EFF_PORTA_UP:
if (chan.effect_param != 0) chan.porta_speed = chan.effect_param * 4;
if (chan->effect_param != 0) chan->porta_speed = chan->effect_param * 4;
break;
case EFF_PORTA_DOWN:
if (chan.effect_param != 0) chan.porta_speed = chan.effect_param * 4;
if (chan->effect_param != 0) chan->porta_speed = chan->effect_param * 4;
break;
case EFF_PORTA_NOTE:
if (note->note > 0)
{
// no fine tune or relative note here, boooy
if (ctx->mod->flags & FLAG_LINEAR_PERIODS) chan.porta_target = __pimp_get_linear_period(note->note + chan.sample->rel_note, 0);
else chan.porta_target = __pimp_get_amiga_period(note->note, 0);
if (ctx->mod->flags & FLAG_LINEAR_PERIODS) chan->porta_target = __pimp_get_linear_period(note->note + chan->sample->rel_note, 0);
else chan->porta_target = __pimp_get_amiga_period(note->note, 0);
/* clamp porta-target period (should not be done for S3M) */
if (chan.porta_target > ctx->mod->period_high_clamp) chan.porta_target = ctx->mod->period_high_clamp;
if (chan.porta_target < ctx->mod->period_low_clamp) chan.porta_target = ctx->mod->period_low_clamp;
if (chan->porta_target > ctx->mod->period_high_clamp) chan->porta_target = ctx->mod->period_high_clamp;
if (chan->porta_target < ctx->mod->period_low_clamp) chan->porta_target = ctx->mod->period_low_clamp;
}
if (chan.effect_param != 0) chan.porta_speed = chan.effect_param * 4;
if (chan->effect_param != 0) chan->porta_speed = chan->effect_param * 4;
break;
/*
case EFF_VIBRATO: break; */
......@@ -201,21 +219,21 @@ $f0-$ff Tone porta
if (note->note > 0)
{
// no fine tune or relative note here, boooy
if (ctx->mod->flags & FLAG_LINEAR_PERIODS) chan.porta_target = __pimp_get_linear_period(note->note + chan.sample->rel_note, 0);
else chan.porta_target = __pimp_get_amiga_period(note->note, 0);
if (ctx->mod->flags & FLAG_LINEAR_PERIODS) chan->porta_target = __pimp_get_linear_period(note->note + chan->sample->rel_note, 0);
else chan->porta_target = __pimp_get_amiga_period(note->note, 0);
/* clamp porta-target period (should not be done for S3M) */
if (chan.porta_target > ctx->mod->period_high_clamp) chan.porta_target = ctx->mod->period_high_clamp;
if (chan.porta_target < ctx->mod->period_low_clamp) chan.porta_target = ctx->mod->period_low_clamp;
if (chan->porta_target > ctx->mod->period_high_clamp) chan->porta_target = ctx->mod->period_high_clamp;
if (chan->porta_target < ctx->mod->period_low_clamp) chan->porta_target = ctx->mod->period_low_clamp;
}
if (chan.effect_param & 0xF0)
if (chan->effect_param & 0xF0)
{
chan.volume_slide_speed = chan.effect_param >> 4;
chan->volume_slide_speed = chan->effect_param >> 4;
}
else if (chan.effect_param & 0x0F)
else if (chan->effect_param & 0x0F)
{
chan.volume_slide_speed = -(chan.effect_param & 0xF);
chan->volume_slide_speed = -(chan->effect_param & 0xF);
}
break;
/*
......@@ -227,7 +245,7 @@ $f0-$ff Tone porta
case EFF_SAMPLE_OFFSET:
if (note->note > 0)
{
mc.sample_cursor = (chan.effect_param * 256) << 12;
mc.sample_cursor = (chan->effect_param * 256) << 12;
/*
if (mc.sample_cursor > mc.sample_length)
{
......@@ -239,32 +257,32 @@ $f0-$ff Tone porta
break;
case EFF_VOLUME_SLIDE:
if (chan.effect_param & 0xF0)
if (chan->effect_param & 0xF0)
{
chan.volume_slide_speed = chan.effect_param >> 4;
chan->volume_slide_speed = chan->effect_param >> 4;
}
else if (chan.effect_param & 0x0F)
else if (chan->effect_param & 0x0F)
{
chan.volume_slide_speed = -(chan.effect_param & 0xF);
chan->volume_slide_speed = -(chan->effect_param & 0xF);
}
break;
/* case EFF_JUMP_ORDER: break; */
case EFF_SET_VOLUME:
chan.volume = chan.effect_param;
if (chan.volume > 64) chan.volume = 64;
chan->volume = chan->effect_param;
if (chan->volume > 64) chan->volume = 64;
volume_dirty = true;
break;
case EFF_BREAK_ROW:
ctx->curr_order++;
ctx->curr_row = (chan.effect_param >> 4) * 10 + (chan.effect_param & 0xF) - 1;
ctx->curr_row = (chan->effect_param >> 4) * 10 + (chan->effect_param & 0xF) - 1;
ctx->curr_pattern = __pimp_module_get_pattern(ctx->mod, __pimp_module_get_order(ctx->mod, ctx->curr_order));
break;
case EFF_MULTI_FX:
switch (chan.effect_param >> 4)
switch (chan->effect_param >> 4)
{
case EFF_AMIGA_FILTER: break;
......@@ -279,34 +297,34 @@ $f0-$ff Tone porta
break;
case EFF_RETRIG_NOTE:
if ((note->effect_parameter & 0xF) != 0) chan.note_retrig = note->effect_parameter & 0xF;
if ((note->effect_parameter & 0xF) != 0) chan->note_retrig = note->effect_parameter & 0xF;
break;
case EFF_FINE_VOLUME_SLIDE_UP:
chan.volume += chan.effect_param & 0xF;
if (chan.volume > 64) chan.volume = 64;
chan->volume += chan->effect_param & 0xF;
if (chan->volume > 64) chan->volume = 64;
volume_dirty = true;
break;
case EFF_FINE_VOLUME_SLIDE_DOWN:
chan.volume -= chan.effect_param & 0xF;
if (chan.volume < 0) chan.volume = 0;
chan->volume -= chan->effect_param & 0xF;
if (chan->volume < 0) chan->volume = 0;
volume_dirty = true;
break;
case EFF_NOTE_DELAY:
chan.note_delay = chan.effect_param & 0xF;
chan.note = note->note;
chan->note_delay = chan->effect_param & 0xF;
chan->note = note->note;
break;
default:
DEBUG_PRINT(("unsupported effect E%X\n", chan.effect_param >> 4));
DEBUG_PRINT(("unsupported effect E%X\n", chan->effect_param >> 4));
}
break;
case EFF_TEMPO:
if (note->effect_parameter < 0x20) ctx->curr_tempo = chan.effect_param;
else __pimp_mod_context_set_bpm(ctx, chan.effect_param);
if (note->effect_parameter < 0x20) ctx->curr_tempo = chan->effect_param;
else __pimp_mod_context_set_bpm(ctx, chan->effect_param);
break;
/*
......@@ -318,14 +336,13 @@ $f0-$ff Tone porta
*/
case EFF_MULTI_RETRIG:
if ((note->effect_parameter & 0xF0) != 0) DEBUG_PRINT(("multi retrig x-parameter != 0 not supported\n"));
if ((note->effect_parameter & 0xF) != 0) chan.note_retrig = note->effect_parameter & 0xF;
if ((note->effect_parameter & 0xF) != 0) chan->note_retrig = note->effect_parameter & 0xF;
break;
/* case EFF_TREMOR: break; */
case EFF_SYNC_CALLBACK:
if (ctx->callback != NULL) ctx->callback(0, chan.effect_param);
else printf("WTF?!");
if (ctx->callback != NULL) ctx->callback(0, chan->effect_param);
break;
/*
......@@ -335,7 +352,7 @@ $f0-$ff Tone porta
*/
default:
DEBUG_PRINT(("unsupported effect %02X\n", chan.effect));
DEBUG_PRINT(("unsupported effect %02X\n", chan->effect));
ASSERT(0);
}
......@@ -343,17 +360,17 @@ $f0-$ff Tone porta
{
if (ctx->mod->flags & FLAG_LINEAR_PERIODS)
{
mc.sample_cursor_delta = __pimp_get_linear_delta(chan.final_period);
mc.sample_cursor_delta = __pimp_get_linear_delta(chan->final_period);
}
else
{
mc.sample_cursor_delta = __pimp_get_amiga_delta(chan.final_period);
mc.sample_cursor_delta = __pimp_get_amiga_delta(chan->final_period);
}
}
if (volume_dirty || chan.vol_env.env != 0)
if (volume_dirty || chan->vol_env.env != 0)
{
mc.volume = (__pimp_channel_get_volume(&chan) * ctx->global_volume) >> 8;
mc.volume = (__pimp_channel_get_volume(chan) * ctx->global_volume) >> 8;
}
}
......@@ -372,26 +389,26 @@ $f0-$ff Tone porta
}
}
static void update_tick(pimp_mod_context *ctx)
STATIC void __pimp_mod_context_update_tick(pimp_mod_context *ctx)
{
if (ctx->mod == NULL) return; // no module active (sound-effects can still be playing, though)
if (ctx->curr_tick == ctx->curr_tempo)
{
update_row(ctx);
__pimp_mod_context_update_row(ctx);
ctx->curr_tick++;
return;
}
for (u32 c = 0; c < ctx->mod->channel_count; ++c)
{
pimp_channel_state &chan = ctx->channels[c];
volatile pimp_mixer_channel_state &mc = ctx->mixer->channels[c];
pimp_channel_state *chan = &ctx->channels[c];
pimp_mixer_channel_state &mc = ctx->mixer->channels[c];
bool period_dirty = false;
bool volume_dirty = false;
switch (chan.volume_command >> 4)
switch (chan->volume_command >> 4)
{
/*
0 Do nothing
......@@ -417,22 +434,22 @@ $f0-$ff Tone porta
case 0x5: break;
case 0x6:
chan.volume -= chan.volume_command & 0xF;
if (chan.volume < 0) chan.volume = 0;
chan->volume -= chan->volume_command & 0xF;
if (chan->volume < 0) chan->volume = 0;
volume_dirty = true;
break;
case 0x7:
chan.volume += chan.volume_command & 0xF;
if (chan.volume > 64) chan.volume = 64;
chan->volume += chan->volume_command & 0xF;
if (chan->volume > 64) chan->volume = 64;
volume_dirty = true;
break;
default:
DEBUG_PRINT(("unsupported volume-command %02X\n", chan.volume_command));
DEBUG_PRINT(("unsupported volume-command %02X\n", chan->volume_command));
}
switch (chan.effect)
switch (chan->effect)
{
case EFF_NONE: break;
......@@ -459,30 +476,30 @@ $f0-$ff Tone porta
period_dirty = true;
/* todo: move to a separate function */
chan.volume += chan.volume_slide_speed;
if (chan.volume > 64) chan.volume = 64;