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 */