bhaal22@643: /** nkeynes@1021: * $Id$ bhaal22@643: * bhaal22@643: * The asla audio driver bhaal22@643: * bhaal22@643: * Copyright (c) 2008 Jonathan Muller bhaal22@643: * bhaal22@643: * This program is free software; you can redistribute it and/or modify bhaal22@643: * it under the terms of the GNU General Public License as published by bhaal22@643: * the Free Software Foundation; either version 2 of the License, or bhaal22@643: * (at your option) any later version. bhaal22@643: * bhaal22@643: * This program is distributed in the hope that it will be useful, bhaal22@643: * but WITHOUT ANY WARRANTY; without even the implied warranty of bhaal22@643: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the bhaal22@643: * GNU General Public License for more details. bhaal22@643: */ bhaal22@643: #include bhaal22@643: #include bhaal22@643: bhaal22@643: /* Use the newer ALSA API */ bhaal22@643: #define ALSA_PCM_NEW_HW_PARAMS_API bhaal22@643: bhaal22@643: #include bhaal22@643: #include "config.h" bhaal22@643: #include "aica/audio.h" bhaal22@643: #include "dream.h" bhaal22@643: bhaal22@643: bhaal22@643: static snd_pcm_t *_soundDevice = NULL; bhaal22@643: static int frame_bytes; bhaal22@643: bhaal22@643: nkeynes@1024: static struct lxdream_config_entry alsa_config[] = { nkeynes@736: {"device", N_("Audio output device"), CONFIG_TYPE_FILE, "default"}, nkeynes@736: {NULL, CONFIG_TYPE_NONE} bhaal22@643: }; bhaal22@643: bhaal22@643: nkeynes@1024: static gboolean audio_alsa_init( ) bhaal22@643: { bhaal22@643: int err; bhaal22@643: snd_pcm_hw_params_t *hw_params; bhaal22@643: snd_pcm_sw_params_t *sw_params; bhaal22@643: snd_pcm_uframes_t frames; bhaal22@643: snd_pcm_uframes_t bufferSize; nkeynes@806: unsigned int rate = DEFAULT_SAMPLE_RATE; nkeynes@697: int format = DEFAULT_SAMPLE_FORMAT; bhaal22@643: int dir; bhaal22@643: bhaal22@643: bhaal22@643: // Open the device we were told to open. bhaal22@643: err = snd_pcm_open( &_soundDevice, alsa_config[0].value, nkeynes@736: SND_PCM_STREAM_PLAYBACK, 0 ); bhaal22@643: bhaal22@643: // Check for error on open. bhaal22@643: if ( err < 0 ) { bhaal22@643: ERROR( "Init: cannot open audio device %s (%s)\n", nkeynes@736: alsa_config[0].value, snd_strerror( err ) ); bhaal22@643: return FALSE; bhaal22@643: } else { bhaal22@643: DEBUG( "Audio device opened successfully." ); bhaal22@643: } bhaal22@643: bhaal22@643: frame_bytes = ( 2 * ( snd_pcm_format_width( SND_PCM_FORMAT_S16_LE ) / 8 ) ); bhaal22@643: bhaal22@643: bhaal22@643: //snd_pcm_hw_params_alloca (&hw_params); bhaal22@643: // Allocate the hardware parameter structure. bhaal22@643: if ( ( err = snd_pcm_hw_params_malloc( &hw_params ) ) < 0 ) { bhaal22@643: ERROR( "Init: cannot allocate hardware parameter structure (%s)\n", nkeynes@736: snd_strerror( err ) ); bhaal22@643: return FALSE; bhaal22@643: } bhaal22@643: bhaal22@643: if ( ( err = snd_pcm_hw_params_any( _soundDevice, hw_params ) ) < 0 ) { bhaal22@643: ERROR( "Init: cannot allocate hardware parameter structure (%s)\n", nkeynes@736: snd_strerror( err ) ); bhaal22@643: return FALSE; bhaal22@643: } bhaal22@643: // Set access to RW interleaved. bhaal22@643: if ( ( err = snd_pcm_hw_params_set_access( _soundDevice, hw_params, nkeynes@736: SND_PCM_ACCESS_RW_INTERLEAVED ) ) nkeynes@736: < 0 ) { bhaal22@643: ERROR( " Init: cannot set access type (%s)\n", snd_strerror( err ) ); bhaal22@643: return FALSE; bhaal22@643: } bhaal22@643: bhaal22@643: if ( ( err = snd_pcm_hw_params_set_format( _soundDevice, hw_params, nkeynes@736: SND_PCM_FORMAT_S16_LE ) ) < nkeynes@736: 0 ) { bhaal22@643: ERROR( "Init: cannot set sample format (%s)\n", snd_strerror( err ) ); bhaal22@643: return FALSE; bhaal22@643: } bhaal22@643: bhaal22@643: err = snd_pcm_hw_params_set_rate_near( _soundDevice, hw_params, &rate, 0 ); bhaal22@643: if ( err < 0 ) { bhaal22@643: ERROR( "Init: Resampling setup failed for playback: %s\n", nkeynes@736: snd_strerror( err ) ); bhaal22@643: return err; bhaal22@643: } bhaal22@643: // Set channels to stereo (2). bhaal22@643: err = snd_pcm_hw_params_set_channels( _soundDevice, hw_params, 2 ); bhaal22@643: if ( err < 0 ) { bhaal22@643: ERROR( "Init: cannot set channel count (%s)\n", snd_strerror( err ) ); bhaal22@643: return FALSE; bhaal22@643: } bhaal22@643: bhaal22@643: // frames = 4410; bhaal22@643: // snd_pcm_hw_params_set_period_size_near( _soundDevice, hw_params, &frames, bhaal22@643: // &dir ); bhaal22@643: bhaal22@643: // Apply the hardware parameters that we've set. bhaal22@643: err = snd_pcm_hw_params( _soundDevice, hw_params ); bhaal22@643: if ( err < 0 ) { bhaal22@643: DEBUG( "Init: cannot set parameters (%s)\n", snd_strerror( err ) ); bhaal22@643: return FALSE; bhaal22@643: } else { bhaal22@643: DEBUG( "Audio device parameters have been set successfully." ); bhaal22@643: } bhaal22@643: bhaal22@643: snd_pcm_hw_params_get_period_size( hw_params, &frames, &dir ); bhaal22@643: DEBUG( "period size = %d\n", frames ); bhaal22@643: bhaal22@643: // Get the buffer size. bhaal22@643: snd_pcm_hw_params_get_buffer_size( hw_params, &bufferSize ); bhaal22@643: DEBUG("Buffer Size = %d\n", bufferSize); bhaal22@643: bhaal22@643: // If we were going to do more with our sound device we would want to store bhaal22@643: // the buffer size so we know how much data we will need to fill it with. bhaal22@643: bhaal22@643: //cout << "Init: Buffer size = " << bufferSize << " frames." << endl; bhaal22@643: bhaal22@643: // Display the bit size of samples. bhaal22@643: //cout << "Init: Significant bits for linear samples = " << snd_pcm_hw_params_get_sbits(hw_params) << endl; bhaal22@643: bhaal22@643: // Free the hardware parameters now that we're done with them. bhaal22@643: snd_pcm_hw_params_free( hw_params ); bhaal22@643: bhaal22@643: // Set the start threshold to reduce inter-buffer gaps bhaal22@643: snd_pcm_sw_params_alloca( &sw_params ); bhaal22@643: snd_pcm_sw_params_current( _soundDevice, sw_params ); nkeynes@880: snd_pcm_sw_params_set_start_threshold( _soundDevice, sw_params, bufferSize ); bhaal22@643: err = snd_pcm_sw_params( _soundDevice, sw_params ); bhaal22@643: if( err < 0 ) { nkeynes@736: ERROR("Unable to set sw params for alsa driver: %s\n", snd_strerror(err)); nkeynes@736: return FALSE; bhaal22@643: } bhaal22@643: bhaal22@643: err = snd_pcm_prepare( _soundDevice ); bhaal22@643: if ( err < 0 ) { bhaal22@643: ERROR( "Init: cannot prepare audio interface for use (%s)\n", nkeynes@736: snd_strerror( err ) ); bhaal22@643: return FALSE; bhaal22@643: } bhaal22@643: return TRUE; bhaal22@643: } bhaal22@643: nkeynes@1024: static gboolean audio_alsa_process_buffer( audio_buffer_t buffer ) bhaal22@643: { bhaal22@643: int err; bhaal22@643: int length; bhaal22@643: bhaal22@643: bhaal22@643: length = buffer->length / frame_bytes; bhaal22@643: bhaal22@643: err = snd_pcm_writei( _soundDevice, buffer->data, length ); bhaal22@643: if( err == -EPIPE ) { nkeynes@736: snd_pcm_prepare( _soundDevice ); bhaal22@643: } else if( err == -ESTRPIPE ) { nkeynes@736: snd_pcm_resume( _soundDevice ); bhaal22@643: } bhaal22@643: bhaal22@643: return TRUE; bhaal22@643: } bhaal22@643: bhaal22@643: nkeynes@1024: static gboolean audio_alsa_shutdown( ) bhaal22@643: { bhaal22@643: return TRUE; bhaal22@643: } bhaal22@643: bhaal22@643: bhaal22@643: nkeynes@1024: static struct audio_driver audio_alsa_driver = { nkeynes@736: "alsa", nkeynes@736: N_("Linux ALSA system driver"), nkeynes@1024: 40, nkeynes@736: DEFAULT_SAMPLE_RATE, nkeynes@736: DEFAULT_SAMPLE_FORMAT, nkeynes@736: audio_alsa_init, nkeynes@736: NULL, nkeynes@736: audio_alsa_process_buffer, nkeynes@736: NULL, nkeynes@736: audio_alsa_shutdown bhaal22@643: }; nkeynes@1024: nkeynes@1024: static gboolean audio_alsa_static_init( void ) nkeynes@1024: { nkeynes@1024: lxdream_register_config_group( "alsa", alsa_config ); nkeynes@1024: audio_register_driver( &audio_alsa_driver ); nkeynes@1024: return TRUE; nkeynes@1024: } nkeynes@1024: nkeynes@1024: DEFINE_PLUGIN( PLUGIN_AUDIO_DRIVER, "alsa", audio_alsa_static_init );