commit e7be81d67814d56df4efaa299738de84427933e4
parent b44d85a29965123b49997731a6396069f7587313
Author: Andrew Alderwick <andrew@alderwick.co.uk>
Date: Fri, 2 Apr 2021 20:43:03 +0100
Implemented stereo sound.
Diffstat:
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;