2021-04-07 20:50:35 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2021 Devine Lu Linvega
|
|
|
|
Copyright (c) 2021 Andrew Alderwick
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and distribute this software for any
|
|
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
WITH REGARD TO THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "uxn.h"
|
2021-04-08 20:14:40 +00:00
|
|
|
#include "apu.h"
|
2021-04-07 20:50:35 +00:00
|
|
|
|
2021-04-25 14:12:45 +00:00
|
|
|
#define NOTE_PERIOD 0x10000
|
|
|
|
#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf)
|
|
|
|
|
|
|
|
/* clang-format off */
|
|
|
|
|
|
|
|
static Uint32 advances[12] = {
|
|
|
|
0x80000, 0x879c8, 0x8facd, 0x9837f, 0xa1451, 0xaadc1,
|
|
|
|
0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c
|
2021-04-07 20:50:35 +00:00
|
|
|
};
|
|
|
|
|
2021-04-25 14:12:45 +00:00
|
|
|
/* clang-format on */
|
|
|
|
|
|
|
|
static Sint32
|
|
|
|
envelope(Apu *c, Uint32 age)
|
2021-04-07 20:50:35 +00:00
|
|
|
{
|
2021-04-25 14:12:45 +00:00
|
|
|
if(!c->r) return 0x0888;
|
|
|
|
if(age < c->a) return 0x0888 * age / c->a;
|
|
|
|
if(age < c->d) return 0x0444 * (2 * c->d - c->a - age) / (c->d - c->a);
|
|
|
|
if(age < c->s) return 0x0444;
|
|
|
|
if(age < c->r) return 0x0444 * (c->r - age) / (c->r - c->s);
|
|
|
|
c->advance = 0;
|
|
|
|
return 0x0000;
|
2021-04-07 20:50:35 +00:00
|
|
|
}
|
|
|
|
|
2021-04-08 20:14:40 +00:00
|
|
|
void
|
2021-04-25 14:12:45 +00:00
|
|
|
apu_render(Apu *c, Sint16 *sample, Sint16 *end)
|
2021-04-07 20:50:35 +00:00
|
|
|
{
|
2021-04-25 14:12:45 +00:00
|
|
|
Sint32 s;
|
|
|
|
if(!c->advance || !c->period) return;
|
|
|
|
while(sample < end) {
|
|
|
|
c->count += c->advance;
|
|
|
|
c->i += c->count / c->period;
|
|
|
|
c->count %= c->period;
|
|
|
|
if(c->i >= c->len) {
|
|
|
|
if(!c->repeat) {
|
|
|
|
c->advance = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
c->i %= c->len;
|
|
|
|
}
|
|
|
|
s = (Sint16)(mempeek16(c->addr, c->i * 2) + 0x8000) * envelope(c, c->age++);
|
|
|
|
*sample++ += s * c->volume_l / 0x8000;
|
|
|
|
*sample++ += s * c->volume_r / 0x8000;
|
|
|
|
}
|
2021-04-07 20:50:35 +00:00
|
|
|
}
|
|
|
|
|
2021-04-08 20:14:40 +00:00
|
|
|
void
|
2021-04-25 14:12:45 +00:00
|
|
|
apu_start(Apu *c, Uint16 adsr, Uint8 pitch)
|
2021-04-07 20:50:35 +00:00
|
|
|
{
|
2021-04-25 14:12:45 +00:00
|
|
|
if(pitch < 108 && c->len)
|
|
|
|
c->advance = advances[pitch % 12] >> (8 - pitch / 12);
|
|
|
|
else {
|
|
|
|
c->advance = 0;
|
|
|
|
return;
|
2021-04-07 20:50:35 +00:00
|
|
|
}
|
2021-04-25 14:12:45 +00:00
|
|
|
c->a = ADSR_STEP * (adsr >> 12);
|
|
|
|
c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a;
|
|
|
|
c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d;
|
|
|
|
c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s;
|
|
|
|
c->age = 0;
|
|
|
|
c->i = 0;
|
|
|
|
if(c->len <= 0x100) /* single cycle mode */
|
|
|
|
c->period = NOTE_PERIOD * 337 / 2 / c->len;
|
|
|
|
else /* sample repeat mode */
|
|
|
|
c->period = NOTE_PERIOD;
|
2021-04-07 20:50:35 +00:00
|
|
|
}
|