uxn

Varvara Ordinator, written in ANSI C(SDL2)
git clone https://git.eamoncaddigan.net/uxn.git
Log | Files | Refs | README | LICENSE

commit e7be81d67814d56df4efaa299738de84427933e4
parent b44d85a29965123b49997731a6396069f7587313
Author: Andrew Alderwick <andrew@alderwick.co.uk>
Date:   Fri,  2 Apr 2021 20:43:03 +0100

Implemented stereo sound.

Diffstat:
Mprojects/examples/dev.time.usm | 6++----
Msrc/emulator.c | 106++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
2 files changed, 67 insertions(+), 45 deletions(-)

diff --git a/projects/examples/dev.time.usm b/projects/examples/dev.time.usm @@ -43,11 +43,9 @@ BRK ~Time.second ~current.second NEQ #01 JNZ BRK ~Time.second =current.second - ~Time.second #03 AND ^$no-sound JNZ - #ffff =Audio.ch1adsr + #1444 =Audio.ch1adsr ~Time.second #1d ADD =Audio.ch1pitch - #ff =Audio.ch1vol - $no-sound + ~Time.second #03 AND #4b MUL #0f ADD =Audio.ch1vol ( clear ) #0080 SCALEX #0080 SCALEY ~needles.sx ~needles.sy #00 ,draw-line JSR2 diff --git a/src/emulator.c b/src/emulator.c @@ -51,26 +51,29 @@ Uint8 font[][8] = { #define SAMPLE_FREQUENCY 48000 -static Uint32 note_periods[12] = { /* middle C (C4) is note 60 */ - (Uint32) 0xfa7e * SAMPLE_FREQUENCY, /* C-1 */ - (Uint32) 0xec6f * SAMPLE_FREQUENCY, - (Uint32) 0xdf2a * SAMPLE_FREQUENCY, /* D-1 */ - (Uint32) 0xd2a4 * SAMPLE_FREQUENCY, - (Uint32) 0xc6d1 * SAMPLE_FREQUENCY, /* E-1 */ - (Uint32) 0xbba8 * SAMPLE_FREQUENCY, /* F-1 */ - (Uint32) 0xb120 * SAMPLE_FREQUENCY, - (Uint32) 0xa72f * SAMPLE_FREQUENCY, /* G-1 */ - (Uint32) 0x9dcd * SAMPLE_FREQUENCY, - (Uint32) 0x94f2 * SAMPLE_FREQUENCY, /* A-1 */ - (Uint32) 0x8c95 * SAMPLE_FREQUENCY, - (Uint32) 0x84b2 * SAMPLE_FREQUENCY /* B-1 */ +static Uint32 note_periods[12] = { + /* middle C (C4) is note 60 */ + (Uint32)0xfa7e * SAMPLE_FREQUENCY, /* C-1 */ + (Uint32)0xec6f * SAMPLE_FREQUENCY, + (Uint32)0xdf2a * SAMPLE_FREQUENCY, /* D-1 */ + (Uint32)0xd2a4 * SAMPLE_FREQUENCY, + (Uint32)0xc6d1 * SAMPLE_FREQUENCY, /* E-1 */ + (Uint32)0xbba8 * SAMPLE_FREQUENCY, /* F-1 */ + (Uint32)0xb120 * SAMPLE_FREQUENCY, + (Uint32)0xa72f * SAMPLE_FREQUENCY, /* G-1 */ + (Uint32)0x9dcd * SAMPLE_FREQUENCY, + (Uint32)0x94f2 * SAMPLE_FREQUENCY, /* A-1 */ + (Uint32)0x8c95 * SAMPLE_FREQUENCY, + (Uint32)0x84b2 * SAMPLE_FREQUENCY /* B-1 */ }; -static struct audio_channel { +typedef struct audio_channel { Uint32 period, count; Sint32 age, a, d, s, r; - Sint16 volume, value; -} channels[4]; + Sint16 value[2]; + Sint8 volume[2], phase; +} Channel; +Channel channels[4]; static SDL_Window *gWindow; static SDL_Renderer *gRenderer; @@ -236,40 +239,59 @@ togglezoom(Uxn *u) redraw(pixels, u); } +Sint16 +audio_envelope(Channel *c) { + if (c->age < c->a) + return 0x0888 * c->age / c->a; + else if (c->age < c->d) + return 0x0444 * (2 * c->d - c->a - c->age) / (c->d - c->a); + else if (c->age < c->s) + return 0x0444; + else if (c->age < c->r) + return 0x0444 * (c->r - c->age) / (c->r - c->s); + else + return 0x0000; +} + void audio_callback(void* userdata, Uint8* stream, int len) { Sint16 *samples = (Sint16 *) stream; int i, j; - len >>= 1; /* use len for number of samples, not bytes */ - for (j = 0; j < len; ++j) samples[j] = 0; + len >>= 2; /* use len for number of samples, not bytes */ + for (j = len * 2 - 1; j >= 0; --j) samples[j] = 0; for (i = 0; i < 4; ++i) { - struct audio_channel *c = &channels[i]; - if (!c->volume) continue; + Channel *c = &channels[i]; if (c->period < (1 << 20)) continue; for (j = 0; j < len; ++j) { c->age += 1; c->count += 1 << 20; while (c->count > c->period) { - int mul = c->value < 0 ? c->volume : -c->volume; + Sint16 mul; c->count -= c->period; - if (c->age < c->a) - c->value = mul * c->age / c->a; - else if (c->age < c->d) - c->value = mul * (2 * c->d - c->a - c->age) / 2 / (c->d - c->a); - else if (c->age < c->s) - c->value = mul / 2; - else if (c->age < c->r) - c->value = mul * (c->r - c->age) / 2 / (c->r - c->s); - else - c->volume = c->value = 0; + c->phase = !c->phase; + mul = (c->phase * 2 - 1) * audio_envelope(c); + c->value[0] = mul * c->volume[0]; + c->value[1] = mul * c->volume[1]; } - samples[j] += c->value; + samples[j * 2] += c->value[0]; + samples[j * 2 + 1] += c->value[1]; } } (void) userdata; } void +silence(void) { + int i; + for (i = 0; i < 4; ++i) { + Channel *c = &channels[i]; + c->volume[0] = 0; + c->volume[1] = 0; + c->period = 0; + } +} + +void quit(void) { free(pixels); @@ -301,11 +323,12 @@ init(void) if(!(pixels = (Uint32 *)malloc(WIDTH * HEIGHT * sizeof(Uint32)))) return error("Pixels", "Failed to allocate memory"); clear(pixels); + silence(); SDL_StartTextInput(); SDL_ShowCursor(SDL_DISABLE); as.freq = SAMPLE_FREQUENCY; as.format = AUDIO_S16; - as.channels = 1; + as.channels = 2; as.callback = audio_callback; as.samples = 2048; audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0); @@ -474,17 +497,18 @@ audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) { Uint8 *m = u->ram.dat; if (b0 & 1) { - Uint16 channel_addr = ptr + (b0 & 0x6); - struct audio_channel *c = &channels[(b0 & 0x6) >> 1]; + Uint16 addr = ptr + (b0 & 0x6); + Channel *c = &channels[(b0 & 0x6) >> 1]; SDL_LockAudioDevice(audio_id); - c->period = note_periods[m[channel_addr + 8] % 12] >> (m[channel_addr + 8] / 12); + c->period = note_periods[m[addr + 8] % 12] >> (m[addr + 8] / 12); c->count %= c->period; - c->volume = m[channel_addr + 9] << 5; + c->volume[0] = (m[addr + 9] >> 4) & 0xf; + c->volume[1] = m[addr + 9] & 0xf; c->age = 0; - c->a = ((m[channel_addr] >> 4) & 0xf) * (SAMPLE_FREQUENCY >> 4); - c->d = c->a + (m[channel_addr] & 0xf) * (SAMPLE_FREQUENCY >> 4); - c->s = c->d + ((m[channel_addr + 1] >> 4) & 0xf) * (SAMPLE_FREQUENCY >> 4); - c->r = c->s + (m[channel_addr + 1] & 0xf) * (SAMPLE_FREQUENCY >> 4); + c->a = (SAMPLE_FREQUENCY >> 4) * ((m[addr] >> 4) & 0xf); + c->d = c->a + (SAMPLE_FREQUENCY >> 4) * (m[addr] & 0xf); + c->s = c->d + (SAMPLE_FREQUENCY >> 4) * ((m[addr + 1] >> 4) & 0xf); + c->r = c->s + (SAMPLE_FREQUENCY >> 4) * (m[addr + 1] & 0xf); SDL_UnlockAudioDevice(audio_id); } return b1;