| ... | ... |
@@ -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); |