|
1 | 1 | #include "minigbs.h"
|
2 |
| -#define MAL_IMPLEMENTATION |
3 |
| -#include "mini_al.h" |
| 2 | +#include <alsa/asoundlib.h> |
4 | 3 | #include <math.h>
|
5 | 4 |
|
6 | 5 | struct chan_len_ctr {
|
@@ -66,17 +65,19 @@ static struct chan {
|
66 | 65 | static size_t nsamples;
|
67 | 66 | static float* samples;
|
68 | 67 | static float* sample_ptr;
|
69 |
| - |
70 |
| -static mal_context audio_ctx; |
71 |
| -static mal_device audio; |
72 |
| -static mal_mutex audio_lock; |
| 68 | +static float* sample_end; |
73 | 69 |
|
74 | 70 | static const int duty_lookup[] = { 0x10, 0x30, 0x3C, 0xCF };
|
75 | 71 | static float logbase;
|
76 | 72 | static float charge_factor;
|
77 | 73 | static float vol_l, vol_r;
|
78 | 74 | static float audio_rate;
|
79 |
| -static bool muted[4]; // not in chan struct to avoid memset(0) across tacks |
| 75 | +static bool muted[4]; // not in chan struct to avoid memset(0) across tracks |
| 76 | +static bool paused; |
| 77 | + |
| 78 | +static snd_pcm_t* pcm; |
| 79 | +static snd_pcm_uframes_t pcm_buffer_size; |
| 80 | +static snd_pcm_uframes_t pcm_period_size; |
80 | 81 |
|
81 | 82 | float hipass(struct chan* c, float sample){
|
82 | 83 | float out = sample - c->capacitor;
|
@@ -300,105 +301,91 @@ bool audio_mute(int chan, int val){
|
300 | 301 | return muted[chan-1];
|
301 | 302 | }
|
302 | 303 |
|
303 |
| -void audio_update(void){ |
304 |
| - memset(samples, 0, nsamples * sizeof(float)); |
305 |
| - |
306 |
| - update_square(0); |
307 |
| - update_square(1); |
308 |
| - update_wave(); |
309 |
| - update_noise(); |
310 |
| - |
311 |
| - for(size_t i = 0; i < nsamples; ++i){ |
312 |
| - samples[i] *= cfg.volume; |
313 |
| - } |
314 |
| - |
315 |
| - sample_ptr = samples + nsamples; |
316 |
| -} |
317 |
| - |
318 |
| -volatile int audio_active; |
319 |
| - |
320 |
| -void audio_pause(bool p){ |
321 |
| - audio_active = !p; |
322 |
| - eventfd_write(evfd_audio_ready, 1); |
323 |
| -} |
324 |
| - |
325 | 304 | void audio_reset(void){
|
326 |
| - audio_pause(true); |
327 |
| - mal_mutex_lock(&audio_ctx, &audio_lock); |
328 |
| - |
329 | 305 | memset(chans, 0, sizeof(chans));
|
330 | 306 | memset(samples, 0, nsamples * sizeof(float));
|
331 | 307 | sample_ptr = samples;
|
| 308 | + sample_end = samples + nsamples; |
332 | 309 | chans[0].val = chans[1].val = -1;
|
333 |
| - |
334 |
| - mal_mutex_unlock(&audio_ctx, &audio_lock); |
335 |
| - audio_pause(false); |
336 | 310 | }
|
337 | 311 |
|
338 |
| -static uint32_t audio_callback(mal_device* ptr, uint32_t len, void* data){ |
339 |
| - uint32_t orig_len = len; |
340 |
| - uint32_t ret = 0; |
341 |
| - len *= 2; |
| 312 | +void audio_pause(bool p){ |
| 313 | + paused = p; |
| 314 | +} |
342 | 315 |
|
343 |
| - mal_mutex_lock(&audio_ctx, &audio_lock); |
| 316 | +int audio_init(struct pollfd** fds, int nfds){ |
| 317 | + snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0); |
| 318 | + snd_pcm_set_params(pcm, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, 2, FREQ, 1, 16667); |
| 319 | + snd_pcm_get_params(pcm, &pcm_buffer_size, &pcm_period_size); |
344 | 320 |
|
345 |
| - if(!audio_active) |
346 |
| - goto out; |
| 321 | + logbase = log(1.059463094f); |
| 322 | + charge_factor = pow(0.999958, 4194304.0 / FREQ); |
347 | 323 |
|
348 |
| - do { |
349 |
| - if(sample_ptr - samples == 0){ |
350 |
| - uint64_t val = 1; |
| 324 | + audio_update_rate(); |
351 | 325 |
|
352 |
| - eventfd_write(evfd_audio_request, val); |
353 |
| - TEMP_FAILURE_RETRY(eventfd_read(evfd_audio_ready, &val)); |
| 326 | + int count = snd_pcm_poll_descriptors_count(pcm); |
| 327 | + *fds = realloc(*fds, (nfds + count) * sizeof(struct pollfd)); |
| 328 | + snd_pcm_poll_descriptors(pcm, (*fds) + nfds, count); |
354 | 329 |
|
355 |
| - if(!audio_active) |
356 |
| - goto out; |
357 |
| - } |
| 330 | + return nfds + count; |
| 331 | +} |
358 | 332 |
|
359 |
| - int n = MIN(len, sample_ptr - samples); |
360 |
| - memcpy(data, samples, n * sizeof(float)); |
361 |
| - memmove(samples, samples + n, (nsamples - n) * sizeof(float)); |
| 333 | +void audio_quit(){ |
| 334 | + snd_pcm_close(pcm); |
| 335 | +} |
362 | 336 |
|
363 |
| - data += (n*4); |
364 |
| - sample_ptr -= n; |
365 |
| - len -= n; |
366 |
| - } while(len); |
| 337 | +void audio_update(struct pollfd* fds, int nfds){ |
| 338 | + static float* buf = NULL; |
| 339 | + const size_t bufsz = (pcm_period_size*2) * sizeof(float); |
367 | 340 |
|
368 |
| - ret = orig_len; |
| 341 | + if(!buf){ |
| 342 | + buf = malloc(bufsz); |
| 343 | + } |
369 | 344 |
|
370 |
| -out: |
371 |
| - mal_mutex_unlock(&audio_ctx, &audio_lock); |
372 |
| - return ret; |
373 |
| -} |
| 345 | + float* p = buf; |
| 346 | + float* end = buf + (bufsz/sizeof(float)); |
374 | 347 |
|
375 |
| -void audio_init(void){ |
| 348 | + uint16_t ev; |
| 349 | + snd_pcm_poll_descriptors_revents(pcm, fds, nfds, &ev); |
376 | 350 |
|
377 |
| - if(mal_context_init(NULL, 0, NULL, &audio_ctx) != MAL_SUCCESS){ |
378 |
| - fprintf(stderr, "mal_context_init failed.\n"); |
379 |
| - exit(1); |
| 351 | + if(!(ev & POLLOUT)){ |
| 352 | + return; |
380 | 353 | }
|
381 | 354 |
|
382 |
| - mal_device_config cfg = mal_device_config_init_playback(mal_format_f32, 2, FREQ, &audio_callback); |
383 |
| - if(mal_device_init(&audio_ctx, mal_device_type_playback, NULL, &cfg, NULL, &audio) != MAL_SUCCESS){ |
384 |
| - fprintf(stderr, "mal_device_init failed.\n"); |
385 |
| - exit(1); |
| 355 | + if(paused){ |
| 356 | + memset(buf, 0, bufsz); |
| 357 | + goto out; |
386 | 358 | }
|
387 | 359 |
|
388 |
| - if(mal_mutex_create(&audio_ctx, &audio_lock) == MAL_FALSE){ |
389 |
| - fprintf(stderr, "mal_mutex_create failed.\n"); |
390 |
| - exit(1); |
391 |
| - } |
| 360 | + while(end - p){ |
| 361 | + if(sample_ptr == sample_end){ |
| 362 | + cpu_frame(); |
392 | 363 |
|
393 |
| - logbase = log(1.059463094f); |
394 |
| - charge_factor = pow(0.999958, 4194304.0 / FREQ); |
| 364 | + memset(samples, 0, nsamples * sizeof(float)); |
395 | 365 |
|
396 |
| - mal_device_start(&audio); |
397 |
| - audio_update_rate(); |
398 |
| - audio_pause(false); |
399 |
| -} |
| 366 | + update_square(0); |
| 367 | + update_square(1); |
| 368 | + update_wave(); |
| 369 | + update_noise(); |
400 | 370 |
|
401 |
| -void audio_quit(void){ |
| 371 | + for(size_t i = 0; i < nsamples; ++i){ |
| 372 | + samples[i] *= cfg.volume; |
| 373 | + } |
| 374 | + |
| 375 | + sample_ptr = samples; |
| 376 | + } |
| 377 | + |
| 378 | + int n = MIN(end - p, sample_end - sample_ptr); |
| 379 | + memcpy(p, sample_ptr, n * sizeof(float)); |
| 380 | + sample_ptr += n; |
| 381 | + p += n; |
| 382 | + } |
| 383 | + |
| 384 | +out:; |
| 385 | + int err = snd_pcm_writei(pcm, buf, pcm_period_size); |
| 386 | + if(err < 0){ |
| 387 | + snd_pcm_recover(pcm, err, 1); |
| 388 | + } |
402 | 389 | }
|
403 | 390 |
|
404 | 391 | void audio_get_notes(uint16_t notes[static 4]){
|
@@ -448,16 +435,20 @@ void audio_update_rate(void){
|
448 | 435 | printf("Audio rate changed: %.4f\n", audio_rate);
|
449 | 436 | }
|
450 | 437 |
|
451 |
| - audio_pause(true); |
452 |
| - mal_mutex_lock(&audio_ctx, &audio_lock); |
| 438 | + size_t new_nsamples = (int)(FREQ / audio_rate) * 2; |
| 439 | + float* new_samples = calloc(new_nsamples, sizeof(float)); |
| 440 | + |
| 441 | + if(samples){ |
| 442 | + memcpy(new_samples, samples, MIN(nsamples, new_nsamples)); |
| 443 | + } |
453 | 444 |
|
454 | 445 | free(samples);
|
455 |
| - nsamples = (int)(FREQ / audio_rate) * 2; |
456 |
| - samples = calloc(nsamples, sizeof(float)); |
457 |
| - sample_ptr = samples; |
| 446 | + samples = new_samples; |
| 447 | + nsamples = new_nsamples; |
458 | 448 |
|
459 |
| - mal_mutex_unlock(&audio_ctx, &audio_lock); |
460 |
| - audio_pause(false); |
| 449 | + // TODO: these should really be adjusted more accurately to not lose samples on speed change |
| 450 | + sample_ptr = samples; |
| 451 | + sample_end = samples + nsamples; |
461 | 452 | }
|
462 | 453 |
|
463 | 454 | void chan_trigger(int i){
|
|
0 commit comments