diff options
Diffstat (limited to 'src/audio.c')
-rw-r--r-- | src/audio.c | 169 |
1 files changed, 104 insertions, 65 deletions
diff --git a/src/audio.c b/src/audio.c index 1ec8108..5120f5b 100644 --- a/src/audio.c +++ b/src/audio.c @@ -32,6 +32,7 @@ #include <stdio.h> #include <SDL.h> #include <SDL_audio.h> +#include <alsa/asoundlib.h> #include <math.h> #include <sys/types.h> #include <unistd.h> @@ -43,30 +44,6 @@ static AudioContext *audio_context = NULL; -static void audio_mix(void *data, Uint8 *stream8, int len) -{ - AudioContext *a = data; - int j; - Sint16 *stream = (Sint16 *)stream8; - Sint16 samp; - - len /= 2; /* Number of samples to write */ - - for ( j=0; j<len; j++ ) { - - stream[j] = 0; - - if ( a->moo_pos < a->moo_len ) { - samp = a->moo_buf[a->moo_pos++]; - stream[j] += samp; - } else { - a->mootex = 0; - } - - } - - a->aplay_fallback = 0; -} void audio_trigger_moo() { @@ -84,43 +61,116 @@ void audio_trigger_moo() a = audio_context; if ( a == NULL ) return; } + printf("Moo!\n"); + snd_pcm_prepare(a->alsa_handle); a->mootex = 1; + a->moo_pos = 0; +} + + +static void *audio_alsa_work(void *data) +{ + AudioContext *a = data; + + while ( !a->finished ) { + + int ret; + + if ( a->moo_pos >= a->moo_len ) { + usleep(1000); + continue; + } + + ret = snd_pcm_writei(a->alsa_handle, a->moo_buf+a->moo_pos, + a->alsa_frames); + + if (ret == -EPIPE) { + fprintf(stderr, "ALSA buffer underrun\n"); + snd_pcm_prepare(a->alsa_handle); + } else if (ret < 0) { + fprintf(stderr, "ALSA write error: %s\n", + snd_strerror(ret)); + } else if (ret != (int)a->alsa_frames) { + fprintf(stderr, "ALSA short write, %d frames\n", + ret); + } else { - if ( a->aplay_fallback ) { - - pid_t pid; - int status; - - printf("Using aplay fallback\n"); - pid = fork(); - if ( !( (pid != 0) && (pid != -1) ) ) { - if ( pid == -1 ) { - fprintf(stderr, "fork() failed.\n"); - return; - } else { - /* Forked successfully, child process */ - execlp("aplay", "aplay", - DATADIR"/openmoocow/moo.wav", NULL); + /* Successful write */ + a->moo_pos += ret; + + if ( a->moo_pos > a->moo_len ) { + while ( snd_pcm_avail(a->alsa_handle) >= 0 ) { + usleep(1000); + } + a->mootex = 0; + printf("Done.\n"); } - } /* else forked successfully, parent process */ - waitpid(pid, &status, 0); - a->mootex = 0; - } else if ( a->moo_pos == a->moo_len ) { - a->moo_pos = 0; + } + } + + snd_pcm_drain(a->alsa_handle); + snd_pcm_close(a->alsa_handle); + + return NULL; } -/* SDL audio initial setup */ +static int audio_open_alsa(AudioContext *a) +{ + int ret; + snd_pcm_t *handle; + snd_pcm_hw_params_t *params; + unsigned int val; + snd_pcm_uframes_t frames; + int dir; + + ret = snd_pcm_open(&handle, "default", + SND_PCM_STREAM_PLAYBACK, 0); + if ( ret < 0 ) { + fprintf(stderr, "Couldn't open audio: %s\n", snd_strerror(ret)); + return FALSE; + } + + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(handle, params); + snd_pcm_hw_params_set_access(handle, params, + SND_PCM_ACCESS_RW_INTERLEAVED); + snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_channels(handle, params, 1); /* One channel */ + val = 44100; /* 44.1 kHz */ + snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); + frames = 32; + snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); + ret = snd_pcm_hw_params(handle, params); + if ( ret < 0 ) { + fprintf(stderr, "Couldn't set parameters: %s\n", + snd_strerror(ret)); + return FALSE; + } + + /* Get the actual period size */ + snd_pcm_hw_params_get_period_size(params, &frames, &dir); + a->alsa_frames = frames; + printf("%i frames per period\n", frames); + + a->alsa_handle = handle; + a->finished = 0; + a->alsa_thread = g_thread_create(audio_alsa_work, a, TRUE, NULL); + + return TRUE; +} + + +/* Audio initial setup */ void audio_setup() { - AudioContext *a; - SDL_AudioSpec fmt; SDL_AudioSpec wave; Uint8 *data; Uint32 dlen; SDL_AudioCVT cvt; + AudioContext *a; /* Create audio context */ a = malloc(sizeof(AudioContext)); @@ -128,32 +178,24 @@ void audio_setup() a->mootex = 1; /* Not ready yet */ audio_context = a; - /* 16-bit mono audio at 44.1 kHz */ - fmt.freq = 44100; - fmt.format = AUDIO_S16; - fmt.channels = 1; - fmt.samples = 512; - fmt.callback = audio_mix; - fmt.userdata = a; - fmt.silence = 0; - - if ( SDL_OpenAudio(&fmt, NULL) < 0 ) { - fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError()); - goto out; /* and use fallback */ + if ( audio_open_alsa(a) ) { + printf("Using ALSA for audio.\n"); + } else { + exit(1); } if ( SDL_LoadWAV(DATADIR"/openmoocow/moo.wav", &wave, &data, &dlen) == NULL ) { fprintf(stderr, "Couldn't load moo sound: %s\n", SDL_GetError()); - goto out; /* and use fallback */ + goto out; } SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_S16, 1, 44100); cvt.buf = malloc(dlen*cvt.len_mult); if ( cvt.buf == NULL ) { fprintf(stderr, "Not enough memory to convert audio \n"); - goto out; /* and use fallback */ + goto out; } memcpy(cvt.buf, data, dlen); cvt.len = dlen; @@ -163,9 +205,6 @@ void audio_setup() a->moo_len = cvt.len_cvt/2 - 2; /* Convert bytes to samples */ a->moo_pos = a->moo_len; /* Play nothing to start with */ a->moo_buf = (Sint16 *)cvt.buf; - a->aplay_fallback = 1; - - SDL_PauseAudio(0); out: a->mootex = 0; /* Ready now */ @@ -177,7 +216,7 @@ void audio_shutdown() if ( a == NULL ) return; - SDL_CloseAudio(); + a->finished = 1; /* Now this can be freed */ free(a); |