... | ... |
@@ -49,11 +49,35 @@ Uint8 font[][8] = { |
49 | 49 |
{0x00, 0x7e, 0x40, 0x7c, 0x40, 0x40, 0x7e, 0x00}, |
50 | 50 |
{0x00, 0x7e, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x00}}; |
51 | 51 |
|
52 |
+#define SAMPLE_FREQUENCY 48000 |
|
53 |
+ |
|
54 |
+static Uint32 note_periods[12] = { /* middle C (C4) is note 60 */ |
|
55 |
+ (Uint32) 0xfa7e * SAMPLE_FREQUENCY, /* C-1 */ |
|
56 |
+ (Uint32) 0xec6f * SAMPLE_FREQUENCY, |
|
57 |
+ (Uint32) 0xdf2a * SAMPLE_FREQUENCY, /* D-1 */ |
|
58 |
+ (Uint32) 0xd2a4 * SAMPLE_FREQUENCY, |
|
59 |
+ (Uint32) 0xc6d1 * SAMPLE_FREQUENCY, /* E-1 */ |
|
60 |
+ (Uint32) 0xbba8 * SAMPLE_FREQUENCY, /* F-1 */ |
|
61 |
+ (Uint32) 0xb120 * SAMPLE_FREQUENCY, |
|
62 |
+ (Uint32) 0xa72f * SAMPLE_FREQUENCY, /* G-1 */ |
|
63 |
+ (Uint32) 0x9dcd * SAMPLE_FREQUENCY, |
|
64 |
+ (Uint32) 0x94f2 * SAMPLE_FREQUENCY, /* A-1 */ |
|
65 |
+ (Uint32) 0x8c95 * SAMPLE_FREQUENCY, |
|
66 |
+ (Uint32) 0x84b2 * SAMPLE_FREQUENCY /* B-1 */ |
|
67 |
+}; |
|
68 |
+ |
|
69 |
+static struct audio_channel { |
|
70 |
+ Uint32 period, count; |
|
71 |
+ int value; |
|
72 |
+ Sint16 volume; |
|
73 |
+} channels[4]; |
|
74 |
+ |
|
52 | 75 |
static SDL_Window *gWindow; |
53 | 76 |
static SDL_Renderer *gRenderer; |
54 | 77 |
static SDL_Texture *gTexture; |
78 |
+static SDL_AudioDeviceID audio_id; |
|
55 | 79 |
static Screen screen; |
56 |
-static Device *devscreen, *devmouse, *devkey, *devctrl; |
|
80 |
+static Device *devscreen, *devmouse, *devkey, *devctrl, *devaudio; |
|
57 | 81 |
|
58 | 82 |
#pragma mark - Helpers |
59 | 83 |
|
... | ... |
@@ -212,6 +236,28 @@ togglezoom(Uxn *u) |
212 | 236 |
redraw(pixels, u); |
213 | 237 |
} |
214 | 238 |
|
239 |
+void |
|
240 |
+audio_callback(void* userdata, Uint8* stream, int len) { |
|
241 |
+ Sint16 *samples = (Sint16 *) stream; |
|
242 |
+ int i, j; |
|
243 |
+ len >>= 1; /* use len for number of samples, not bytes */ |
|
244 |
+ for (j = 0; j < len; ++j) samples[j] = 0; |
|
245 |
+ for (i = 0; i < 4; ++i) { |
|
246 |
+ struct audio_channel *c = &channels[i]; |
|
247 |
+ if (!c->volume) continue; |
|
248 |
+ if (c->period < (1 << 20)) continue; |
|
249 |
+ for (j = 0; j < len; ++j) { |
|
250 |
+ c->count += 1 << 20; |
|
251 |
+ while (c->count > c->period) { |
|
252 |
+ c->value = !c->value; |
|
253 |
+ c->count -= c->period; |
|
254 |
+ } |
|
255 |
+ samples[j] += (c->value * 2 - 1) * c->volume; |
|
256 |
+ } |
|
257 |
+ } |
|
258 |
+ (void) userdata; |
|
259 |
+} |
|
260 |
+ |
|
215 | 261 |
void |
216 | 262 |
quit(void) |
217 | 263 |
{ |
... | ... |
@@ -229,7 +275,8 @@ quit(void) |
229 | 275 |
int |
230 | 276 |
init(void) |
231 | 277 |
{ |
232 |
- if(SDL_Init(SDL_INIT_VIDEO) < 0) |
|
278 |
+ SDL_AudioSpec as; |
|
279 |
+ if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) |
|
233 | 280 |
return error("Init", SDL_GetError()); |
234 | 281 |
gWindow = SDL_CreateWindow("Uxn", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH * ZOOM, HEIGHT * ZOOM, SDL_WINDOW_SHOWN); |
235 | 282 |
if(gWindow == NULL) |
... | ... |
@@ -245,6 +292,15 @@ init(void) |
245 | 292 |
clear(pixels); |
246 | 293 |
SDL_StartTextInput(); |
247 | 294 |
SDL_ShowCursor(SDL_DISABLE); |
295 |
+ as.freq = SAMPLE_FREQUENCY; |
|
296 |
+ as.format = AUDIO_S16; |
|
297 |
+ as.channels = 1; |
|
298 |
+ as.callback = audio_callback; |
|
299 |
+ as.samples = 2048; |
|
300 |
+ audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0); |
|
301 |
+ if(!audio_id) |
|
302 |
+ return error("Audio", SDL_GetError()); |
|
303 |
+ SDL_PauseAudioDevice(audio_id, 0); |
|
248 | 304 |
screen.x1 = PAD * 8; |
249 | 305 |
screen.x2 = WIDTH - PAD * 8 - 1; |
250 | 306 |
screen.y1 = PAD * 8; |
... | ... |
@@ -405,8 +461,16 @@ file_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) |
405 | 461 |
Uint8 |
406 | 462 |
audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) |
407 | 463 |
{ |
408 |
- (void)u; |
|
409 |
- printf("%04x - %02x,%02x\n", ptr, b0, b1); |
|
464 |
+ Uint8 *m = u->ram.dat; |
|
465 |
+ if (b0 & 1) { |
|
466 |
+ Uint16 channel_addr = ptr + (b0 & 0x6); |
|
467 |
+ struct audio_channel *c = &channels[(b0 & 0x6) >> 1]; |
|
468 |
+ SDL_LockAudioDevice(audio_id); |
|
469 |
+ c->period = note_periods[m[channel_addr + 8] % 12] >> (m[channel_addr + 8] / 12); |
|
470 |
+ c->count %= c->period; |
|
471 |
+ c->volume = m[channel_addr + 9] << 5; |
|
472 |
+ SDL_UnlockAudioDevice(audio_id); |
|
473 |
+ } |
|
410 | 474 |
return b1; |
411 | 475 |
} |
412 | 476 |
|
... | ... |
@@ -517,7 +581,7 @@ main(int argc, char **argv) |
517 | 581 |
devkey = portuxn(&u, "key", ppnil); |
518 | 582 |
devmouse = portuxn(&u, "mouse", ppnil); |
519 | 583 |
portuxn(&u, "file", file_poke); |
520 |
- portuxn(&u, "audio", audio_poke); |
|
584 |
+ devaudio = portuxn(&u, "audio", audio_poke); |
|
521 | 585 |
portuxn(&u, "midi", ppnil); |
522 | 586 |
portuxn(&u, "datetime", datetime_poke); |
523 | 587 |
portuxn(&u, "---", ppnil); |