File indexing completed on 2024-04-14 04:49:16
0001 /* This file is part of the KDE project 0002 Copyright (C) Alexander Kern <alex.kern@gmx.de> 0003 0004 Adapted to support both ALSA V0.x and V1.x APIs for PCM calls 0005 (Philip Nelson <teamdba@scotdb.com> 2004-03-15) 0006 0007 based on mpg123 , Anders Semb Hermansen <ahermans@vf.telia.no>, 0008 Jaroslav Kysela <perex@jcu.cz>, Ville Syrjala <syrjala@sci.fi> 0009 0010 This library is free software; you can redistribute it and/or 0011 modify it under the terms of the GNU Library General Public 0012 License version 2 as published by the Free Software Foundation. 0013 0014 This library is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 Library General Public License for more details. 0018 0019 You should have received a copy of the GNU Library General Public License 0020 along with this library; see the file COPYING.LIB. If not, write to 0021 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0022 Boston, MA 02110-1301, USA. 0023 */ 0024 0025 #include "audio.h" 0026 #include "../include/wm_struct.h" 0027 #include "../include/wm_config.h" 0028 0029 #include <config-alsa.h> 0030 0031 #ifdef HAVE_ALSA 0032 0033 #define _BSD_SOURCE /* strdup */ 0034 #define _DEFAULT_SOURCE /* stop glibc whining about the previous line */ 0035 0036 #include <alsa/asoundlib.h> 0037 0038 static char *device = NULL; 0039 static snd_pcm_t *handle; 0040 0041 static snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ 0042 0043 #if (SND_LIB_MAJOR < 1) 0044 int rate = 44100; /* stream rate */ 0045 int new_rate; 0046 int channels = 2; /* count of channels */ 0047 int buffer_time = 2000000; /* ring buffer length in us */ 0048 int period_time = 100000; /* period time in us */ 0049 #else 0050 unsigned int rate = 44100; /* stream rate */ 0051 unsigned int new_rate; 0052 int channels = 2; /* count of channels */ 0053 unsigned int buffer_time = 2000000; /* ring buffer length in us */ 0054 unsigned int period_time = 100000; /* period time in us */ 0055 #endif 0056 0057 #if (SND_LIB_MAJOR < 1) 0058 snd_pcm_sframes_t buffer_size; 0059 snd_pcm_sframes_t period_size; 0060 #else 0061 snd_pcm_uframes_t buffer_size; 0062 snd_pcm_uframes_t period_size; 0063 #endif 0064 0065 0066 int alsa_open(void); 0067 int alsa_close(void); 0068 int alsa_stop(void); 0069 int alsa_play(struct wm_cdda_block *blk); 0070 int alsa_state(struct wm_cdda_block *blk); 0071 struct audio_oops* setup_alsa(const char *dev, const char *ctl); 0072 0073 static int set_hwparams(snd_pcm_hw_params_t *params, 0074 snd_pcm_access_t accesspar) 0075 { 0076 int err, dir; 0077 0078 /* choose all parameters */ 0079 err = snd_pcm_hw_params_any(handle, params); 0080 if (err < 0) { 0081 ERRORLOG("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); 0082 return err; 0083 } 0084 /* set the interleaved read/write format */ 0085 err = snd_pcm_hw_params_set_access(handle, params, accesspar); 0086 if (err < 0) { 0087 ERRORLOG("Access type not available for playback: %s\n", snd_strerror(err)); 0088 return err; 0089 } 0090 /* set the sample format */ 0091 err = snd_pcm_hw_params_set_format(handle, params, format); 0092 if (err < 0) { 0093 ERRORLOG("Sample format not available for playback: %s\n", snd_strerror(err)); 0094 return err; 0095 } 0096 /* set the count of channels */ 0097 err = snd_pcm_hw_params_set_channels(handle, params, channels); 0098 if (err < 0) { 0099 ERRORLOG("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err)); 0100 return err; 0101 } 0102 /* set the stream rate */ 0103 #if (SND_LIB_MAJOR < 1) 0104 err = new_rate = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0); 0105 #else 0106 new_rate = rate; 0107 err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0); 0108 #endif 0109 if (err < 0) { 0110 ERRORLOG("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); 0111 return err; 0112 } 0113 if (new_rate != rate) { 0114 ERRORLOG("Rate does not match (requested %iHz, get %iHz)\n", rate, new_rate); 0115 return -EINVAL; 0116 } 0117 /* set the buffer time */ 0118 #if (SND_LIB_MAJOR < 1) 0119 err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir); 0120 #else 0121 err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); 0122 #endif 0123 if (err < 0) { 0124 ERRORLOG("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)); 0125 return err; 0126 } 0127 #if (SND_LIB_MAJOR < 1) 0128 buffer_size = snd_pcm_hw_params_get_buffer_size(params); 0129 #else 0130 err = snd_pcm_hw_params_get_buffer_size(params, &buffer_size); 0131 if (err < 0) { 0132 ERRORLOG("Unable to get buffer size : %s\n", snd_strerror(err)); 0133 return err; 0134 } 0135 #endif 0136 DEBUGLOG("buffersize %lu\n", buffer_size); 0137 0138 /* set the period time */ 0139 #if (SND_LIB_MAJOR < 1) 0140 err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir); 0141 #else 0142 err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); 0143 #endif 0144 if (err < 0) { 0145 ERRORLOG("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err)); 0146 return err; 0147 } 0148 0149 #if (SND_LIB_MAJOR < 1) 0150 period_size = snd_pcm_hw_params_get_period_size(params, &dir); 0151 #else 0152 err = snd_pcm_hw_params_get_period_size(params, &period_size, &dir); 0153 if (err < 0) { 0154 ERRORLOG("Unable to get hw period size: %s\n", snd_strerror(err)); 0155 } 0156 #endif 0157 0158 DEBUGLOG("period_size %lu\n", period_size); 0159 0160 /* write the parameters to device */ 0161 err = snd_pcm_hw_params(handle, params); 0162 if (err < 0) { 0163 ERRORLOG("Unable to set hw params for playback: %s\n", snd_strerror(err)); 0164 return err; 0165 } 0166 return 0; 0167 } 0168 0169 static int set_swparams(snd_pcm_sw_params_t *swparams) 0170 { 0171 int err; 0172 0173 /* get the current swparams */ 0174 err = snd_pcm_sw_params_current(handle, swparams); 0175 if (err < 0) { 0176 ERRORLOG("Unable to determine current swparams for playback: %s\n", snd_strerror(err)); 0177 return err; 0178 } 0179 /* start the transfer when the buffer is full */ 0180 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size); 0181 if (err < 0) { 0182 ERRORLOG("Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); 0183 return err; 0184 } 0185 /* allow the transfer when at least period_size samples can be processed */ 0186 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size); 0187 if (err < 0) { 0188 ERRORLOG("Unable to set avail min for playback: %s\n", snd_strerror(err)); 0189 return err; 0190 } 0191 /* align all transfers to 1 sample */ 0192 err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); 0193 if (err < 0) { 0194 ERRORLOG("Unable to set transfer align for playback: %s\n", snd_strerror(err)); 0195 return err; 0196 } 0197 /* write the parameters to the playback device */ 0198 err = snd_pcm_sw_params(handle, swparams); 0199 if (err < 0) { 0200 ERRORLOG("Unable to set sw params for playback: %s\n", snd_strerror(err)); 0201 return err; 0202 } 0203 return 0; 0204 } 0205 0206 int alsa_open( void ) 0207 { 0208 int err; 0209 0210 snd_pcm_hw_params_t *hwparams; 0211 snd_pcm_sw_params_t *swparams; 0212 0213 DEBUGLOG("alsa_open\n"); 0214 0215 snd_pcm_hw_params_alloca(&hwparams); 0216 snd_pcm_sw_params_alloca(&swparams); 0217 0218 if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/)) < 0 ) { 0219 ERRORLOG("open failed: %s\n", snd_strerror(err)); 0220 return -1; 0221 } 0222 0223 if((err = set_hwparams(hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { 0224 ERRORLOG("Setting of hwparams failed: %s\n", snd_strerror(err)); 0225 return -1; 0226 } 0227 if((err = set_swparams(swparams)) < 0) { 0228 ERRORLOG("Setting of swparams failed: %s\n", snd_strerror(err)); 0229 return -1; 0230 } 0231 0232 return 0; 0233 } 0234 0235 int alsa_close( void ) 0236 { 0237 int err; 0238 0239 DEBUGLOG("alsa_close\n"); 0240 0241 err = alsa_stop(); 0242 0243 #if (SND_LIB_MAJOR < 1) 0244 err = snd_pcm_close(handle); 0245 #else 0246 err = snd_pcm_close(handle); 0247 #endif 0248 0249 free(device); 0250 0251 return err; 0252 } 0253 0254 /* 0255 * Play some audio and pass a status message upstream, if applicable. 0256 * Returns 0 on success. 0257 */ 0258 int 0259 alsa_play(struct wm_cdda_block *blk) 0260 { 0261 signed short *ptr; 0262 int err = 0, frames; 0263 0264 ptr = (signed short *)blk->buf; 0265 frames = blk->buflen / (channels * 2); 0266 DEBUGLOG("play %i frames, %lu bytes\n", frames, blk->buflen); 0267 while (frames > 0) { 0268 err = snd_pcm_writei(handle, ptr, frames); 0269 0270 if (err == -EAGAIN) 0271 continue; 0272 if(err == -EPIPE) { 0273 err = snd_pcm_prepare(handle); 0274 continue; 0275 } else if (err < 0) 0276 break; 0277 0278 ptr += err * channels; 0279 frames -= err; 0280 DEBUGLOG("played %i, rest %i\n", err, frames); 0281 } 0282 0283 if (err < 0) { 0284 ERRORLOG("alsa_write failed: %s\n", snd_strerror(err)); 0285 err = snd_pcm_prepare(handle); 0286 0287 if (err < 0) { 0288 ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err)); 0289 } 0290 blk->status = WM_CDM_CDDAERROR; 0291 return err; 0292 } 0293 0294 return 0; 0295 } 0296 0297 /* 0298 * Stop the audio immediately. 0299 */ 0300 int 0301 alsa_stop( void ) 0302 { 0303 int err; 0304 0305 DEBUGLOG("alsa_stop\n"); 0306 0307 err = snd_pcm_drop(handle); 0308 if (err < 0) { 0309 ERRORLOG("Unable to drop pcm stream: %s\n", snd_strerror(err)); 0310 } 0311 0312 err = snd_pcm_prepare(handle); 0313 if (err < 0) { 0314 ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err)); 0315 } 0316 0317 return err; 0318 } 0319 0320 static struct audio_oops alsa_oops = { 0321 .wmaudio_open = alsa_open, 0322 .wmaudio_close = alsa_close, 0323 .wmaudio_play = alsa_play, 0324 .wmaudio_stop = alsa_stop, 0325 .wmaudio_state = NULL, 0326 .wmaudio_balvol = NULL 0327 }; 0328 0329 struct audio_oops* 0330 setup_alsa(const char *dev, const char *ctl) 0331 { 0332 static int init_complete = 0; 0333 0334 DEBUGLOG("setup_alsa\n"); 0335 0336 if(init_complete) { 0337 alsa_close(); 0338 init_complete = 0; 0339 } 0340 0341 if(dev && strlen(dev) > 0) { 0342 device = strdup(dev); 0343 } else { 0344 device = strdup("plughw:0,0"); /* playback device */ 0345 } 0346 0347 if(!alsa_open()) 0348 init_complete = 1; 0349 else 0350 return NULL; 0351 0352 return &alsa_oops; 0353 } 0354 0355 #endif /* HAVE_ALSA */