/* * audio.c * * Moo like a cow * * (c) 2008-2009 Thomas White * * This file is part of OpenMooCow - accelerometer moobox simulator * * OpenMooCow is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenMooCow is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenMooCow. If not, see . * */ /* SDL's headers don't like this. Grr. */ //#ifdef HAVE_CONFIG_H //#include //#endif #include #include #include #include #include #include #include #include #include #include #include #include "types.h" #include "audio.h" static AudioContext *audio_context = NULL; void audio_trigger_moo() { AudioContext *a = audio_context; if ( a->mootex != 0 ) { printf("Mootex says 'no'\n"); return; } if ( a == NULL ) { /* Try to open the audio again */ printf("Trying to open the audio again...\n"); audio_setup(); a = audio_context; if ( a == NULL ) return; } /* Prevent another moo, until we're done */ a->mootex = 1; printf("Moo!\n"); /* Switch from "setup" to "prepare" mode, reading for writing data */ snd_pcm_prepare(a->alsa_handle); /* Start the moo */ a->moo_pos = 0; } static void *audio_alsa_work(void *data) { AudioContext *a = data; while ( !a->finished ) { int ret; snd_pcm_uframes_t tp; if ( a->moo_pos >= a->moo_len ) { usleep(1000); continue; } /* Play the full number of frames, or the number of frames * remaining to be played - whichever is the lowest */ if ( a->moo_len - a->moo_pos > a->alsa_frames ) { tp = a->alsa_frames; } else { tp = a->moo_len - a->moo_pos; } ret = snd_pcm_writei(a->alsa_handle, a->moo_buf+a->moo_pos, tp); 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)tp) { fprintf(stderr, "ALSA short write, %d frames\n", ret); } else { /* Successful write */ a->moo_pos += ret; if ( a->moo_pos == a->moo_len ) { /* We have finished writing. Now sleep * until the hardware has caught up. */ snd_pcm_hwsync(a->alsa_handle); //while ( snd_pcm_avail(a->alsa_handle) >= 0 ) { // usleep(1000); //} /* Drop back to "setup" state */ snd_pcm_drop(a->alsa_handle); /* Not it's safe to moo again */ a->mootex = 0; } } } snd_pcm_drain(a->alsa_handle); snd_pcm_close(a->alsa_handle); return NULL; } 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; 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() { SDL_AudioSpec wave; Uint8 *data; Uint32 dlen; SDL_AudioCVT cvt; AudioContext *a; /* Create audio context */ a = malloc(sizeof(AudioContext)); assert(a != NULL); a->mootex = 1; /* Not ready yet */ audio_context = a; 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; } 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; } memcpy(cvt.buf, data, dlen); cvt.len = dlen; SDL_ConvertAudio(&cvt); SDL_FreeWAV(data); 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; out: a->mootex = 0; /* Ready now */ } void audio_shutdown() { AudioContext *a = audio_context; if ( a == NULL ) return; a->finished = 1; /* Now this can be freed */ free(a); }