2021-05-12 21:28:45 -04:00
|
|
|
#include "../uxn.h"
|
2021-12-28 16:47:35 -05:00
|
|
|
#include "audio.h"
|
2021-05-12 21:28:45 -04:00
|
|
|
|
2021-04-07 16:50:35 -04: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.
|
|
|
|
*/
|
|
|
|
|
2021-05-19 03:30:39 -04:00
|
|
|
#define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025)
|
2021-04-25 10:12:45 -04:00
|
|
|
#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf)
|
|
|
|
|
2022-03-17 12:59:36 -04:00
|
|
|
typedef struct {
|
|
|
|
Uint8 *addr;
|
|
|
|
Uint32 count, advance, period, age, a, d, s, r;
|
|
|
|
Uint16 i, len;
|
|
|
|
Sint8 volume[2];
|
|
|
|
Uint8 pitch, repeat;
|
|
|
|
} UxnAudio;
|
|
|
|
|
2021-04-25 10:12:45 -04:00
|
|
|
/* clang-format off */
|
|
|
|
|
|
|
|
static Uint32 advances[12] = {
|
|
|
|
0x80000, 0x879c8, 0x8facd, 0x9837f, 0xa1451, 0xaadc1,
|
|
|
|
0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c
|
2021-04-07 16:50:35 -04:00
|
|
|
};
|
|
|
|
|
2021-12-29 12:11:03 -05:00
|
|
|
UxnAudio uxn_audio[POLYPHONY];
|
2021-12-28 16:47:35 -05:00
|
|
|
|
2021-04-25 10:12:45 -04:00
|
|
|
/* clang-format on */
|
|
|
|
|
|
|
|
static Sint32
|
2021-12-29 12:11:03 -05:00
|
|
|
envelope(UxnAudio *c, Uint32 age)
|
2021-04-07 16:50:35 -04:00
|
|
|
{
|
2021-04-25 10:12:45 -04: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 16:50:35 -04:00
|
|
|
}
|
|
|
|
|
2021-07-17 05:13:21 -04:00
|
|
|
int
|
2022-03-17 12:59:36 -04:00
|
|
|
audio_render(int instance, Sint16 *sample, Sint16 *end)
|
2021-04-07 16:50:35 -04:00
|
|
|
{
|
2022-03-17 12:59:36 -04:00
|
|
|
UxnAudio *c = &uxn_audio[instance];
|
2021-04-25 10:12:45 -04:00
|
|
|
Sint32 s;
|
2021-07-17 05:13:21 -04:00
|
|
|
if(!c->advance || !c->period) return 0;
|
2021-04-25 10:12:45 -04:00
|
|
|
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;
|
2021-08-20 17:45:39 -04:00
|
|
|
break;
|
2021-04-25 10:12:45 -04:00
|
|
|
}
|
|
|
|
c->i %= c->len;
|
|
|
|
}
|
2021-05-04 02:26:36 -04:00
|
|
|
s = (Sint8)(c->addr[c->i] + 0x80) * envelope(c, c->age++);
|
2021-04-26 14:49:34 -04:00
|
|
|
*sample++ += s * c->volume[0] / 0x180;
|
|
|
|
*sample++ += s * c->volume[1] / 0x180;
|
2021-04-25 10:12:45 -04:00
|
|
|
}
|
2022-03-17 12:59:36 -04:00
|
|
|
if(!c->advance) audio_finished_handler(instance);
|
2021-07-17 05:13:21 -04:00
|
|
|
return 1;
|
2021-04-07 16:50:35 -04:00
|
|
|
}
|
|
|
|
|
2021-04-08 16:14:40 -04:00
|
|
|
void
|
2022-03-17 12:59:36 -04:00
|
|
|
audio_start(int instance, Device *d)
|
2021-04-07 16:50:35 -04:00
|
|
|
{
|
2022-03-17 12:59:36 -04:00
|
|
|
UxnAudio *c = &uxn_audio[instance];
|
|
|
|
Uint16 addr, adsr;
|
|
|
|
Uint8 pitch;
|
|
|
|
DEVPEEK16(adsr, 0x8);
|
|
|
|
DEVPEEK16(c->len, 0xa);
|
|
|
|
DEVPEEK16(addr, 0xc);
|
|
|
|
if(c->len > 0x10000 - addr)
|
|
|
|
c->len = 0x10000 - addr;
|
|
|
|
c->addr = &d->u->ram[addr];
|
|
|
|
c->volume[0] = d->dat[0xe] >> 4;
|
|
|
|
c->volume[1] = d->dat[0xe] & 0xf;
|
|
|
|
c->repeat = !(d->dat[0xf] & 0x80);
|
|
|
|
pitch = d->dat[0xf] & 0x7f;
|
2021-04-25 10:12:45 -04:00
|
|
|
if(pitch < 108 && c->len)
|
|
|
|
c->advance = advances[pitch % 12] >> (8 - pitch / 12);
|
|
|
|
else {
|
|
|
|
c->advance = 0;
|
|
|
|
return;
|
2021-04-07 16:50:35 -04:00
|
|
|
}
|
2021-04-25 10:12:45 -04: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 16:50:35 -04:00
|
|
|
}
|
2021-04-25 16:52:39 -04:00
|
|
|
|
|
|
|
Uint8
|
2022-03-17 12:59:36 -04:00
|
|
|
audio_get_vu(int instance)
|
2021-04-25 16:52:39 -04:00
|
|
|
{
|
2022-03-17 12:59:36 -04:00
|
|
|
UxnAudio *c = &uxn_audio[instance];
|
2021-08-01 16:16:03 -04:00
|
|
|
int i;
|
2021-08-20 17:43:35 -04:00
|
|
|
Sint32 sum[2] = {0, 0};
|
2021-04-26 14:49:34 -04:00
|
|
|
if(!c->advance || !c->period) return 0;
|
2022-01-03 21:04:09 -05:00
|
|
|
for(i = 0; i < 2; i++) {
|
2021-08-20 17:43:35 -04:00
|
|
|
if(!c->volume[i]) continue;
|
|
|
|
sum[i] = 1 + envelope(c, c->age) * c->volume[i] / 0x800;
|
2021-04-25 16:52:39 -04:00
|
|
|
if(sum[i] > 0xf) sum[i] = 0xf;
|
|
|
|
}
|
|
|
|
return (sum[0] << 4) | sum[1];
|
|
|
|
}
|
2022-03-17 12:59:36 -04:00
|
|
|
|
|
|
|
Uint16
|
|
|
|
audio_get_position(int instance)
|
|
|
|
{
|
|
|
|
UxnAudio *c = &uxn_audio[instance];
|
|
|
|
return c->i;
|
|
|
|
}
|