Skip to content

Commit

Permalink
libxm: fix period calculation of high octaves in Amiga mode
Browse files Browse the repository at this point in the history
In octaves 6 and 7, there was not enough precision left to accurately
compute the period, because too many bits were discarded during the
scaling down.

To fix this, scale the frequency table by 1024 after switching to
uint32_t, and adjust calculations accordingly.

Co-developed with @bryc.
  • Loading branch information
rasky committed Apr 19, 2021
1 parent e7a6f14 commit 013d814
Showing 1 changed file with 12 additions and 9 deletions.
21 changes: 12 additions & 9 deletions src/play.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ static void xm_sample(xm_context_t*, float*, float*);
#define XM_TRIGGER_KEEP_SAMPLE_POSITION (1 << 2)
#define XM_TRIGGER_KEEP_ENVELOPE (1 << 3)

static const uint16_t amiga_frequencies[] = {
1712, 1616, 1525, 1440, /* C-2, C#2, D-2, D#2 */
1357, 1281, 1209, 1141, /* E-2, F-2, F#2, G-2 */
1077, 1017, 961, 907, /* G#2, A-2, A#2, B-2 */
856, /* C-3 */
#define AMIGA_FREQ_SCALE 1024

static const uint32_t amiga_frequencies[] = {
1712*AMIGA_FREQ_SCALE, 1616*AMIGA_FREQ_SCALE, 1525*AMIGA_FREQ_SCALE, 1440*AMIGA_FREQ_SCALE, /* C-2, C#2, D-2, D#2 */
1357*AMIGA_FREQ_SCALE, 1281*AMIGA_FREQ_SCALE, 1209*AMIGA_FREQ_SCALE, 1141*AMIGA_FREQ_SCALE, /* E-2, F-2, F#2, G-2 */
1077*AMIGA_FREQ_SCALE, 1017*AMIGA_FREQ_SCALE, 961*AMIGA_FREQ_SCALE, 907*AMIGA_FREQ_SCALE, /* G#2, A-2, A#2, B-2 */
856*AMIGA_FREQ_SCALE, /* C-3 */
};

static const float multi_retrig_add[] = {
Expand Down Expand Up @@ -304,7 +306,7 @@ static float xm_amiga_period(float note) {
unsigned int intnote = note;
uint8_t a = intnote % 12;
int8_t octave = note / 12.f - 2;
uint16_t p1 = amiga_frequencies[a], p2 = amiga_frequencies[a + 1];
int32_t p1 = amiga_frequencies[a], p2 = amiga_frequencies[a + 1];

if(octave > 0) {
p1 >>= octave;
Expand All @@ -314,7 +316,7 @@ static float xm_amiga_period(float note) {
p2 <<= (-octave);
}

return XM_LERP(p1, p2, note - intnote);
return XM_LERP(p1, p2, note - intnote) / AMIGA_FREQ_SCALE;
}

static float xm_amiga_frequency(float period) {
Expand All @@ -339,7 +341,7 @@ static float xm_frequency(xm_context_t* ctx, float period, float note_offset) {
uint8_t a;
int8_t octave;
float note;
uint16_t p1, p2;
int32_t p1, p2;

switch(ctx->module.frequency_type) {

Expand All @@ -356,6 +358,7 @@ static float xm_frequency(xm_context_t* ctx, float period, float note_offset) {
a = octave = 0;

/* Find the octave of the current period */
period *= AMIGA_FREQ_SCALE;
if(period > amiga_frequencies[0]) {
--octave;
while(period > (amiga_frequencies[0] << (-octave))) --octave;
Expand Down Expand Up @@ -383,7 +386,7 @@ static float xm_frequency(xm_context_t* ctx, float period, float note_offset) {
}

if(XM_DEBUG && (p1 < period || p2 > period)) {
DEBUG("%i <= %f <= %i should hold but doesn't, this is a bug", p2, period, p1);
DEBUG("%li <= %f <= %li should hold but doesn't, this is a bug", p2, period, p1);
}

note = 12.f * (octave + 2) + a + XM_INVERSE_LERP(p1, p2, period);
Expand Down

0 comments on commit 013d814

Please sign in to comment.