root/rat/trunk/auddev_alsa.c

Revision 4395, 39.9 KB (checked in by piers, 5 years ago)

Further minor tweaks to try to fix bugs when using PulseAudio? with ALSA - as reported by Christoph Willing. Also commented out alsa dump commands so RAT doesn't emit debug info for normal use.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * FILE:    auddev_alsa.c
3 * PROGRAM: RAT ALSA 0.9+/final audio driver.
4 * AUTHOR:  Steve Smith  (ALSA 0.9+/final)
5 *          Robert Olson (ALSA 0.5)
6 *
7 * Copyright (c) 2003 University of Sydney
8 * Copyright (c) 2000 Argonne National Laboratory
9 * All rights reserved.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 *
14 * $Revision$
15 * $Date$
16 *
17 */
18
19#define ALSA_PCM_NEW_HW_PARAMS_API
20#define ALSA_PCM_NEW_SW_PARAMS_API
21#include <alsa/asoundlib.h>
22#include <stdio.h>
23#include <stdarg.h>
24
25#include "config_unix.h"
26#include "audio_types.h"
27#include "auddev_alsa.h"
28#include "debug.h"
29
30
31/*
32 * Structure that keeps track of the cards we know about. Rat wants a linear
33 * list of cards, so we'll give it that.
34 *
35 * This is filled in during the init step.
36 */
37
38typedef struct RatCardInfo_t
39{
40    char *name;
41    int card_number;
42    char *pcm_device;
43    char *pcm_mixer;
44} RatCardInfo;
45
46#define MAX_RAT_CARDS 128
47
48static RatCardInfo ratCards[MAX_RAT_CARDS];
49static int nRatCards = 0;
50static int mixer_state_change = 0;
51
52/* Define ALSA mixer identifiers.  We use these to lookup the volume
53 * controls on the mixer.  This feels like a bit of a hack, but
54 * appears to be what everybody else uses. */
55#define RAT_ALSA_MIXER_PCM_NAME "PCM"
56#define RAT_ALSA_MIXER_MASTER_NAME "Master"
57#define RAT_ALSA_MIXER_CAPTURE_NAME "Capture"
58#define RAT_ALSA_MIXER_LINE_NAME "Line"
59#define RAT_ALSA_MIXER_MIC_NAME "Mic"
60#define RAT_ALSA_MIXER_CD_NAME "CD"
61
62/*
63 * The list of input ports to choose between.  These are scanned from the
64 * mixer device.
65 */
66#define MAX_RAT_DEVICES 32
67
68typedef struct _port_t {
69    audio_port_details_t details;
70    snd_mixer_selem_id_t *sid;
71    snd_mixer_elem_t* smixer_elem;
72    int priority;
73} port_t;
74
75static port_t iports[MAX_RAT_DEVICES];
76static unsigned num_iports;
77
78/*
79 * The list of output ports to choose between.
80 */
81static audio_port_details_t out_ports[] = {
82    {0, RAT_ALSA_MIXER_PCM_NAME},
83    {1, RAT_ALSA_MIXER_MASTER_NAME}
84};
85
86static audio_port_t oport = 0;
87
88#define NUM_OUT_PORTS (sizeof(out_ports)/(sizeof(out_ports[0])))
89
90/*
91 * Current open audio device
92 */
93
94typedef struct _pcm_stream_t {
95    snd_pcm_t *handle;
96    snd_pcm_uframes_t buffer_size;
97    snd_pcm_uframes_t period_size;
98    int channels;
99} pcm_stream_t;
100
101static struct current_t {
102    int index;
103    RatCardInfo *info;
104    unsigned bytes_per_block;
105    pcm_stream_t tx;
106    pcm_stream_t rx;
107    audio_port_t iport;
108    snd_mixer_t *mixer;
109    snd_mixer_elem_t *txgain;
110    snd_mixer_elem_t *rxgain;
111    audio_port_t txport;
112} current;
113
114static void clear_current()
115{
116    current.tx.handle = NULL;
117    current.rx.handle = NULL;
118    current.mixer =  NULL;
119    current.index = -1;
120    current.info = NULL;
121    current.iport = (audio_port_t)NULL;
122}
123
124
125/*
126 * Utility funcs
127 */
128static char *encodingToString[] = {
129    "PCMU",
130    "PCMA",
131    "S8",
132    "U8",
133    "S16"
134};
135
136// Trimmed down Error macros
137#define CHECKERR(msg) \
138{ \
139  if (err < 0) \
140  { \
141    debug_msg(msg); \
142    debug_msg("snd_strerror : %s\n", snd_strerror(err)); \
143    return FALSE; \
144  } \
145}
146#define CHECKOPENERR(msg) \
147{ \
148  if (err < 0) \
149  { \
150    debug_msg(msg); \
151    debug_msg("snd_strerror : %s\n", snd_strerror(err)); \
152    snd_pcm_close(stream->handle); \
153    stream->handle=NULL; \
154    return FALSE; \
155  } \
156}
157
158
159
160static int mapformat(deve_e encoding)
161{
162    int format = -1;
163    switch (encoding)
164    {
165    case DEV_PCMU:
166        format = SND_PCM_FORMAT_MU_LAW;
167        break;
168      case DEV_PCMA:
169        format = SND_PCM_FORMAT_A_LAW;
170        break;
171    case DEV_S8:
172        format = SND_PCM_FORMAT_S8;
173        break;
174    case DEV_U8:
175        format = SND_PCM_FORMAT_U8;
176        break;
177    case DEV_S16:
178        format = SND_PCM_FORMAT_S16;
179        break;
180    }
181    return format;
182}
183
184__attribute__((unused)) static char* mapstate(snd_pcm_state_t s)
185{
186    switch (s) {
187      case SND_PCM_STATE_OPEN:
188        return "SND_PCM_STATE_OPEN";
189      case SND_PCM_STATE_SETUP:
190        return "SND_PCM_STATE_SETUP";
191      case SND_PCM_STATE_PREPARED:
192        return "SND_PCM_STATE_PREPARED";
193      case SND_PCM_STATE_RUNNING:
194        return "SND_PCM_STATE_RUNNING";
195      case SND_PCM_STATE_XRUN:
196        return "SND_PCM_STATE_XRUN";
197      case SND_PCM_STATE_DRAINING:
198        return "SND_PCM_STATE_DRAINING";
199      case SND_PCM_STATE_PAUSED:
200        return "SND_PCM_STATE_PAUSED";
201      case SND_PCM_STATE_SUSPENDED:
202        return "SND_PCM_STATE_SUSPENDED";
203      default:
204        return "UNKNOWN!!!";
205    }
206}
207
208static void dump_audio_format(audio_format *f)
209{
210    if (f == 0)
211        debug_msg("    <null>\n");
212    else
213        debug_msg("encoding=%s sample_rate=%d bits_per_sample=%d "
214                  "channels=%d bytes_per_block=%d\n",
215                  encodingToString[f->encoding],
216                  f->sample_rate, f->bits_per_sample,
217                  f->channels, f->bytes_per_block);
218}
219
220
221static void  __attribute__((unused)) dump_alsa_current(snd_pcm_t *handle)
222{
223    int err;
224    snd_output_t *out;
225
226    err = snd_output_stdio_attach(&out, stderr, 0);
227    snd_output_printf(out, "--- MY IO\n");
228
229    err = snd_pcm_dump_setup(handle, out);
230
231    snd_output_printf(out, "--- SW\n");
232    err = snd_pcm_dump_sw_setup(handle, out);
233
234    snd_output_printf(out, "--- HW\n");
235    err = snd_pcm_dump_hw_setup(handle, out);
236
237    snd_output_printf(out, "--- DONE\n");
238    snd_output_close(out);
239    }
240
241
242/* *** Alsa driver implementation. *** */
243
244static int open_stream(RatCardInfo *info, pcm_stream_t *stream,
245                       snd_pcm_stream_t type, audio_format *fmt)
246    {
247    int err;
248    int dir=0;
249    size_t bsize;
250    snd_pcm_uframes_t frames;
251    unsigned int rrate;
252    snd_pcm_hw_params_t *hw_params;
253    snd_pcm_sw_params_t *sw_params;
254    /* Set avail min inside a software configuration container */
255    /* Can we tweak this up/down?*/
256    //snd_pcm_uframes_t    avail_min=100; //4   
257    snd_pcm_uframes_t    avail_min=4;   
258    snd_pcm_uframes_t    xfer_align=1; 
259
260    // Open type (Capture or Playback)
261    err = snd_pcm_open(&stream->handle, info->pcm_device,
262                       type, SND_PCM_NONBLOCK);
263    if (err<0) {
264      debug_msg("snd_pcm_open failed for: %s\n",info->pcm_device);
265      return FALSE;
266    }
267    debug_msg("ALSA:snd_pcm_open ok for: %s\n",info->pcm_device);
268
269    snd_pcm_hw_params_alloca (&hw_params);
270
271    err = snd_pcm_hw_params_any (stream->handle, hw_params);
272    CHECKOPENERR("Failed to initialise HW parameters");
273
274    err = snd_pcm_hw_params_set_access(stream->handle, hw_params,
275                                       SND_PCM_ACCESS_RW_INTERLEAVED);
276    CHECKOPENERR("Failed to set interleaved access");
277
278    err = snd_pcm_hw_params_set_format (stream->handle, hw_params,
279                                        mapformat(fmt->encoding));
280    CHECKOPENERR("Failed to set encoding");
281
282    err = snd_pcm_hw_params_set_channels (stream->handle, hw_params,
283                                          fmt->channels);
284    CHECKOPENERR("Failed to set channels");
285    stream->channels = fmt->channels;
286
287    rrate = fmt->sample_rate;
288    err = snd_pcm_hw_params_set_rate_near (stream->handle, hw_params,
289                                           &rrate, &dir);
290    CHECKOPENERR("Failed to set sample rate");
291    if (rrate != fmt->sample_rate) {
292        debug_msg("Error: ALSA rate set to %d when we wanted %d\n",
293                rrate, fmt->sample_rate);
294        snd_pcm_close(stream->handle);
295        stream->handle=NULL;
296        return FALSE;
297    }
298
299    // Setup the buffer size. This stuff's all in frames (one frame
300    // is number of channel * sample - e.g one stereo frame is two samples).
301    // We can't convert with the helper functions (snd_pcm_bytes_to_frames())
302    // at this point as they require a working handle, and ours isn't setup
303    // yet. We use a factor RAT_ALSA_BUFFER_DIVISOR which is buffer size in
304    // millisec(10^-3) - it seems we need
305    // We don't actually do anything with these values anyway.
306    bsize = snd_pcm_format_size (mapformat(fmt->encoding),
307                                 fmt->sample_rate / RAT_ALSA_BUFFER_DIVISOR);
308    debug_msg("Bsize1 == %d\n", bsize);
309    bsize = (fmt->sample_rate * (fmt->bits_per_sample/8) * fmt->channels* RAT_ALSA_BUFFER_DIVISOR)/ (1000);
310    debug_msg("Bsize2 == %d\n", bsize);
311    bsize = (fmt->sample_rate*RAT_ALSA_BUFFER_DIVISOR)/1000;
312
313    frames = bsize;
314    debug_msg("sample_rate, bytes_per_block == %d, %d\n", fmt->sample_rate, fmt->bytes_per_block);
315    debug_msg("Bsize == %d\n", bsize);
316    err = snd_pcm_hw_params_set_buffer_size_near(stream->handle, hw_params,
317                                                 &frames);
318    CHECKOPENERR("Failed to set buffer size");
319
320    stream->buffer_size = frames;
321    debug_msg("Return Bsize == %d\n", stream->buffer_size);
322
323    frames=  fmt->bytes_per_block / ((fmt->bits_per_sample/8)* fmt->channels);
324    frames= bsize /(RAT_ALSA_BUFFER_DIVISOR/10);
325    err = snd_pcm_hw_params_set_period_size_near(stream->handle, hw_params,
326                                                 &frames, &dir);
327    CHECKOPENERR("Failed to set period size");
328
329    stream->period_size = frames;
330    debug_msg("Returned Period == %d\n", stream->period_size);
331
332    err = snd_pcm_hw_params (stream->handle, hw_params);
333    CHECKOPENERR("Failed to install HW parameters");
334
335
336    // ALSA software settings
337    snd_pcm_sw_params_alloca(&sw_params);
338    err = snd_pcm_sw_params_current(stream->handle, sw_params);
339    CHECKOPENERR("Failed to initialise SW params");
340
341    err = snd_pcm_sw_params_set_start_threshold(stream->handle, sw_params,
342                                                stream->period_size);
343    CHECKOPENERR("Failed to set threshold value");
344
345    err = snd_pcm_sw_params_set_avail_min(stream->handle, sw_params, stream->period_size);
346    CHECKOPENERR("Failed to set min available value");
347
348    err = snd_pcm_sw_params_set_xfer_align(stream->handle,sw_params,xfer_align);
349    CHECKOPENERR("Failed to set xfer align value");
350
351    err = snd_pcm_sw_params(stream->handle, sw_params);
352    CHECKOPENERR("Failed to set SW params");
353   
354    snd_pcm_sw_params_get_boundary(sw_params, &avail_min);
355    debug_msg("snd_pcm_sw_params_get_boundary %d\n",avail_min);
356    snd_pcm_sw_params_set_stop_threshold(stream->handle,sw_params, avail_min);
357    snd_pcm_sw_params_get_stop_threshold(sw_params, &avail_min);
358    debug_msg("snd_pcm_sw_params_get_stop_threshold %d\n",avail_min);
359    snd_pcm_sw_params_get_silence_size(sw_params, &avail_min);
360    debug_msg("snd_pcm_sw_params_get_silence_size %d\n",avail_min);
361    snd_pcm_sw_params_get_avail_min(sw_params, &avail_min);
362    debug_msg("min available value %d\n",avail_min);
363
364    snd_output_t *output = NULL;
365    snd_output_stdio_attach(&output, stdout, 0);
366    //snd_pcm_dump_setup(stream->handle, output);
367    //snd_pcm_dump(stream->handle, output);
368    return TRUE;
369}
370
371
372
373// Open a named mixer
374static int open_volume_ctl(char *name, snd_mixer_elem_t **ctl)
375{
376  snd_mixer_selem_id_t *sid;
377  int err=0;
378
379  snd_mixer_selem_id_alloca(&sid);
380
381  // FIXME? Find the appropriate mixer element.  This feels really wrong,
382  // there has to be another way to do this.
383  snd_mixer_selem_id_set_index (sid, 0);
384  snd_mixer_selem_id_set_name (sid, name);
385
386  *ctl = snd_mixer_find_selem(current.mixer, sid);
387  if (*ctl == NULL ) {
388   debug_msg("Couldn't find mixer control element (name:%s)\n",name);
389   return FALSE;
390  }
391
392  if (snd_mixer_selem_has_playback_volume(*ctl)) {
393    debug_msg("Got volume control %s of type PLAY\n", name);
394    // FIXME: Does this always work?
395    snd_mixer_selem_set_playback_volume_range (*ctl, 0, 100);
396
397    if (snd_mixer_selem_has_playback_switch( *ctl ) ) {
398      if (snd_mixer_selem_set_playback_switch_all(*ctl, 1) < 0) {
399        debug_msg("Failed to switch on playback volume");
400        return FALSE;
401      }
402    }
403
404  } else if (snd_mixer_selem_has_capture_volume(*ctl)) {
405    debug_msg("Got volume control %s of type CAPTURE\n", name);
406    snd_mixer_selem_set_capture_volume_range (*ctl, 0, 100);
407
408    if (snd_mixer_selem_has_capture_switch(*ctl)) {
409      err = snd_mixer_selem_set_capture_switch_all(*ctl, 1);
410      if (err<0) {
411        debug_msg("Failed to switch on capture volume");
412        return FALSE;
413      }
414    }
415
416  } else {
417    debug_msg("Unknown mixer type %s set\n", name);
418    return FALSE;
419  }
420
421  return TRUE;
422}
423
424
425// Open and initialise the system mixer
426
427static int porder(const void *a, const void *b)
428{
429    return (((port_t*)a)->priority - ((port_t*)b)->priority);
430}
431
432/* Not used for now. It seems to cause problems in mbus, however it
433 * may be useful for handling mixer updates from external sources
434 *
435static int
436mixer_callback (snd_mixer_t *mixer, unsigned int mask, snd_mixer_elem_t *elem)
437{
438  mixer_state_change = 1;
439  debug_msg("mixer_callback\n");
440  return 0;
441}
442*/
443
444static int setup_mixers()
445{
446    snd_mixer_elem_t *elem, *selem;
447    snd_mixer_selem_id_t *sid;
448    int err;
449    unsigned i;
450    int need_cap_switch=1;
451   
452    snd_mixer_selem_id_alloca(&sid);
453
454    err = snd_mixer_open (&current.mixer, 0);
455    if (err < 0) {
456      debug_msg("Failed to open the mixer(snd err: %s)",snd_strerror(err));
457      current.mixer=NULL;
458      return FALSE;
459    }
460
461    err = snd_mixer_attach(current.mixer, current.info->pcm_mixer);
462    if (err < 0) {
463      debug_msg("Failed to attach the mixer(snd err: %s)",snd_strerror(err));
464      snd_mixer_close(current.mixer);
465      current.mixer=NULL;
466      return FALSE;
467    }
468
469    err = snd_mixer_selem_register(current.mixer, NULL, NULL);
470    if (err < 0) {
471      debug_msg("Failed to register the mixer(snd err: %s)",snd_strerror(err));
472      snd_mixer_close(current.mixer);
473      current.mixer=NULL;
474      return FALSE;
475    }
476   
477    /* Not used for now - see above */
478    /* snd_mixer_set_callback (current.mixer, mixer_callback); */
479
480    err = snd_mixer_load(current.mixer);
481    if (err < 0) {
482      debug_msg("Failed to load the mixer(snd err: %s)",snd_strerror(err));
483      snd_mixer_close(current.mixer);
484      current.mixer=NULL;
485      return FALSE;
486    }
487
488    /* Get the playback and capture volume controls
489     * If Capture open fails it ptobably means we have a capture card
490     * which does not support capture_switch - in which case we set the
491     * capture level of the selected input device.
492     */
493    oport = 0;
494    if (!open_volume_ctl(RAT_ALSA_MIXER_PCM_NAME, &current.txgain)) {
495      oport = 1;
496      if (!open_volume_ctl(RAT_ALSA_MIXER_MASTER_NAME, &current.txgain)) {
497        oport = 0;
498        snd_mixer_close(current.mixer);
499        current.mixer=NULL;
500        return FALSE;
501      }
502    }
503    if (!open_volume_ctl(RAT_ALSA_MIXER_CAPTURE_NAME, &current.rxgain)) {
504      need_cap_switch=0;
505      current.rxgain=0;
506    }
507
508    num_iports = 0;
509
510    /* We now scan the mixer for recording controls.  We're interested in
511     * the controls that are part of a group (i.e. radio-button types) that
512     * can be flipped between.
513     */
514    for (elem = snd_mixer_first_elem (current.mixer);
515        elem && (num_iports < MAX_RAT_DEVICES);
516        elem = snd_mixer_elem_next (elem))
517    {
518      if (!snd_mixer_selem_is_active(elem) ||
519           snd_mixer_selem_is_enumerated(elem))
520         continue;
521
522      sid = (snd_mixer_selem_id_t*)malloc(snd_mixer_selem_id_sizeof());
523      snd_mixer_selem_get_id( elem, sid );
524      /* Find the Simple mixer element */
525      selem = snd_mixer_find_selem(current.mixer, sid );
526
527      int gid = snd_mixer_selem_get_capture_group(selem);
528      const char *name = snd_mixer_selem_get_name(selem);
529
530      debug_msg("Trying CAPTURE element '%s' of group %d \n", name, gid);
531
532      /* The code below is minimal so it works for card without capture switch */
533      if (snd_mixer_selem_has_capture_volume(selem) ||
534          snd_mixer_selem_has_capture_switch(selem) )
535      //    snd_mixer_selem_is_active (elem) &&
536      //    (snd_mixer_selem_has_capture_switch(elem)) &&
537      //    snd_mixer_selem_has_capture_switch_exclusive(elem))
538      {
539        // FIXME: It's theoretically possible that there would be more
540        // than one capture group, but RAT isn't really equipped to handle
541        // the case so we'll just ignore it for now.
542
543        debug_msg("Got CAPTURE element '%s' of group %d\n", name, gid);
544
545        snprintf(iports[num_iports].details.name, AUDIO_PORT_NAME_LENGTH,
546            "%s", name);
547        iports[num_iports].smixer_elem = selem;
548        //iports[num_iports].sid = sid;
549    debug_msg("Found %s selem:%d (sid:%d)\n",iports[num_iports].details.name,selem,iports[num_iports].sid );
550
551        // The principle of least-surprise means that we should present
552        // the ports in the same order as the other drivers.  As we're
553        // more flexible about retrieving the mixer ports we need to
554        // attempt to reorder the list, so we assign a priority and
555        // sort the list at the end.
556        if (strstr(name, RAT_ALSA_MIXER_MIC_NAME) == name) {
557          iports[num_iports].priority = 100;
558        } else if (strstr(name, RAT_ALSA_MIXER_LINE_NAME) == name) {
559          iports[num_iports].priority = 50;
560        } else if (strstr(name, RAT_ALSA_MIXER_CD_NAME) == name) {
561          iports[num_iports].priority = 30;
562        } else {
563          iports[num_iports].priority = 10;
564        }
565        num_iports++;
566      }
567    }
568
569    qsort(iports, num_iports, sizeof(port_t), porder);
570
571    // Now it's sorted we need to set the port ID to the index, allowing us
572    // a fast lookup for port-IDs
573    for (i=0; i<num_iports; i++) {
574        iports[i].details.port = i;
575    }
576
577    return TRUE;
578}
579
580
581int alsa_audio_open(audio_desc_t ad, audio_format *infmt, audio_format *outfmt)
582{
583    int err;
584
585    debug_msg("Audio open ad == %d\n", ad);
586    debug_msg("Input format:\n");
587    dump_audio_format(infmt);
588    debug_msg("Output format:\n");
589    dump_audio_format(outfmt);
590   
591    //clear_current();
592    if (current.tx.handle != NULL) {
593        debug_msg( "Attempt to open a device while another is open\n");
594        return FALSE;
595    }
596    current.index = ad;
597    current.info = ratCards + ad;
598    current.bytes_per_block = infmt->bytes_per_block;
599
600    if (!open_stream(current.info, &current.tx,
601                     SND_PCM_STREAM_PLAYBACK, outfmt)) {
602        alsa_audio_close(ad);
603        debug_msg( "Failed to open device for playback\n");
604        return FALSE;
605    }
606    debug_msg( "Opened device for playback\n");
607
608    if (!open_stream(current.info, &current.rx,
609                     SND_PCM_STREAM_CAPTURE, infmt)) {
610        alsa_audio_close(ad);
611        debug_msg( "Failed to open device for capture\n");
612        return FALSE;
613    }
614    debug_msg( "Opened device for capture\n");
615
616    if (setup_mixers() == FALSE) {
617      alsa_audio_close(ad);
618        debug_msg( "Failed to set mixer levels \n");
619      return FALSE;
620    }
621
622    err = snd_pcm_prepare(current.tx.handle);
623    if (err<0) {
624      debug_msg("Failed to prepare playback");
625      alsa_audio_close(ad);
626      return FALSE;
627    }
628
629
630    err = snd_pcm_start(current.rx.handle);
631    if (err<0) {
632      debug_msg("Failed to start PCM capture");
633      alsa_audio_close(ad);
634      return FALSE;
635    }
636
637    return TRUE;
638}
639
640/*
641 * Shutdown.
642 */
643void alsa_audio_close(audio_desc_t ad)
644{
645    int err;
646   
647    if (current.index != ad) {
648        fprintf(stderr, "Index to close (%d) doesn't match current(%d)\n",
649                ad, current.index);
650        return;
651    }
652
653    debug_msg("Closing device \"%s\"\n", current.info->name);
654
655    if (current.tx.handle != NULL ) {
656            err = snd_pcm_close(current.tx.handle);
657            if (err<0) debug_msg("Error closing playback PCM");
658    }
659
660    if (current.rx.handle != NULL ) {
661            err = snd_pcm_close(current.rx.handle);
662            if (err<0) debug_msg("Error closing capture PCM");
663    }
664
665    // Close mixer
666    if (current.mixer !=  NULL ) {
667        err = snd_mixer_close(current.mixer);
668        if (err<0) debug_msg("Error closing mixer");
669    }
670
671    clear_current();
672}
673
674/*
675 * Flush input buffer.
676 */
677void alsa_audio_drain(audio_desc_t ad __attribute__((unused)))
678{
679    int err;
680
681    debug_msg("audio_drain\n");
682    err = snd_pcm_drain(current.rx.handle);
683    if (err<0) debug_msg("Problem draining input\n");
684}
685
686static void handle_mixer_events(snd_mixer_t *mixer_handle)
687{
688  int count, err;
689  struct pollfd *fds;
690  int num_revents = 0;
691  unsigned short revents;
692
693  /* Get count of poll descriptors for mixer handle */
694  if ((count = snd_mixer_poll_descriptors_count(mixer_handle)) < 0) {
695    debug_msg("Error in snd_mixer_poll_descriptors_count(%d)\n", count);
696    return;
697  }
698
699  fds =(struct pollfd*)calloc(count, sizeof(struct pollfd));
700  if (fds == NULL) {
701    debug_msg("snd_mixer fds calloc err\n");
702    return;
703  }
704
705  if ((err = snd_mixer_poll_descriptors(mixer_handle, fds, count)) < 0){
706    debug_msg ("snd_mixer_poll_descriptors err=%d\n", err);
707  free(fds);
708    return;
709  }
710
711  if (err != count){
712    debug_msg ("snd_mixer_poll_descriptors (err(%d) != count(%d))\n",err,count);
713  free(fds);
714    return;
715  }
716
717  num_revents = poll(fds, count, 1);
718
719  /* Graceful handling of signals recvd in poll() */
720  if (num_revents < 0 && errno == EINTR)
721    num_revents = 0;
722
723  if (num_revents > 0) {
724    if (snd_mixer_poll_descriptors_revents(mixer_handle, fds, count, &revents) >= 0) {
725      if (revents & POLLNVAL)
726        debug_msg ("snd_mixer_poll_descriptors (POLLNVAL)\n");
727      if (revents & POLLERR)
728        debug_msg ("snd_mixer_poll_descriptors (POLLERR)\n");
729      if (revents & POLLIN)
730        snd_mixer_handle_events(mixer_handle);
731    }
732  }
733  free(fds);
734}
735   
736
737
738/*
739 * Set capture gain.
740 */
741void alsa_audio_set_igain(audio_desc_t ad, int gain)
742{
743    int err;
744    debug_msg("Set igain %d %d\n", ad, gain);
745
746    if (current.rxgain) {
747      if (snd_mixer_selem_has_capture_switch(current.rxgain)) {
748        err = snd_mixer_selem_set_capture_volume_all(current.rxgain, gain);
749        if (err<0) debug_msg("Failed to set capture volume\n");
750      } else {
751        // Or try setting capture level of selected input
752        debug_msg("Attempting to set input element capture volume\n");
753        err = snd_mixer_selem_set_capture_volume_all(iports[current.iport].smixer_elem, gain);
754        if(err<0) debug_msg("Failed to set capture volume\n");
755      }
756    } else {
757        // Or try setting capture level of selected input
758        debug_msg("current.rxgain=0 - Attempting to set input element capture volume\n");
759        err = snd_mixer_selem_set_capture_volume_all(iports[current.iport].smixer_elem, gain);
760        if(err<0) debug_msg("Failed to set capture volume\n");
761    }
762handle_mixer_events(current.mixer);
763//    mixer_state_change = 1;
764}
765
766
767/*
768 * Get capture gain.
769 */
770int alsa_audio_get_igain(audio_desc_t ad)
771{
772    long igain=0;
773    int err;
774    debug_msg("Get igain %d\n", ad);
775
776    if (current.rxgain) {
777      if (snd_mixer_selem_has_capture_switch(current.rxgain)) {
778        err = snd_mixer_selem_get_capture_volume(current.rxgain,
779                                                 SND_MIXER_SCHN_MONO, &igain);
780        if(err<0) debug_msg("Failed to get capture volume");
781      } else {
782        // Or try getting capture level of selected input
783        err = snd_mixer_selem_get_capture_volume(iports[current.iport].smixer_elem, SND_MIXER_SCHN_MONO, &igain);
784        if(err<0) debug_msg("Failed to get capture volume");
785      }
786    } else {
787        // Or try getting capture level of selected input
788        err = snd_mixer_selem_get_capture_volume(iports[current.iport].smixer_elem, SND_MIXER_SCHN_MONO, &igain);
789        if(err<0) debug_msg("Failed to get capture volume");
790    }
791
792    return (int)igain;
793}
794
795int alsa_audio_duplex(audio_desc_t ad __attribute__((unused)))
796{
797    return TRUE; // FIXME: ALSA always duplex?
798}
799
800/*
801 * Set play gain.
802 */
803void alsa_audio_set_ogain(audio_desc_t ad, int vol)
804{
805    int err;
806
807    debug_msg("Set ogain %d %d\n", ad, vol);
808
809    err = snd_mixer_selem_set_playback_switch_all(current.txgain, 1);
810    if(err<0) debug_msg("Failed to switch on playback volume");
811
812    err = snd_mixer_selem_set_playback_volume_all(current.txgain, vol);
813    if(err<0) debug_msg("Couldn't set mixer playback volume");
814
815handle_mixer_events(current.mixer);
816//    mixer_state_change = 1;
817}
818
819/*
820 * Get play gain.
821 */
822int
823alsa_audio_get_ogain(audio_desc_t ad)
824{
825    long ogain=0;
826    int err;
827
828    debug_msg("Get ogain %d\n", ad);
829    err = snd_mixer_selem_get_playback_volume(current.txgain,
830                                             SND_MIXER_SCHN_MONO, &ogain);
831    if(err<0) debug_msg("Failed to get capture volume");
832
833    return (int)ogain;
834}
835
836
837/*
838 * Record audio data.
839 */
840
841int alsa_audio_read(audio_desc_t ad __attribute__((unused)),
842                u_char *buf, int bytes)
843{
844    snd_pcm_sframes_t fread, bread, avail;
845    int err;
846    long read_interval;
847    struct timeval          curr_time;
848    static struct timeval          last_read_time;
849
850    gettimeofday(&curr_time, NULL);
851    if (mixer_state_change) {
852      mixer_state_change=0;
853      handle_mixer_events (current.mixer);
854        debug_msg("Handling mixer event\n");
855    }
856
857    snd_pcm_sframes_t frames = snd_pcm_bytes_to_frames(current.rx.handle,current.bytes_per_block);
858    read_interval = (curr_time.tv_sec  - last_read_time.tv_sec) * 1000000 + (curr_time.tv_usec - last_read_time.tv_usec);
859    avail = snd_pcm_avail_update(current.rx.handle);
860    //debug_msg("Frames avail to be read=%d, time diff=%ld, want: %d frames, %d bytes (bytepb %d\n",avail, read_interval, frames, bytes, current.bytes_per_block);
861    last_read_time.tv_sec=curr_time.tv_sec;
862    last_read_time.tv_usec=curr_time.tv_usec;
863
864    if (avail < frames)
865      return 0;
866
867    frames = snd_pcm_bytes_to_frames(current.rx.handle,bytes);
868    fread = snd_pcm_readi(current.rx.handle, buf, frames);
869
870    if (fread >= 0) {
871        // Normal case
872        bread = snd_pcm_frames_to_bytes(current.rx.handle, fread);
873        gettimeofday(&curr_time, NULL);
874        read_interval = (curr_time.tv_sec  - last_read_time.tv_sec) * 1000000 + (curr_time.tv_usec - last_read_time.tv_usec);
875        //debug_msg("Read %d bytes (%d frames) took %ld usecs\n", bread, fread, read_interval);
876        return bread;
877    }
878
879   // Something happened
880    switch (fread)
881        {
882        case -EAGAIN:
883          // Normal when non-blocking
884    gettimeofday(&curr_time, NULL);
885    read_interval = (curr_time.tv_sec  - last_read_time.tv_sec) * 1000000 + (curr_time.tv_usec - last_read_time.tv_usec);
886    debug_msg("failed %d Read %d bytes (%d frames) took %ld usecs\n", fread, bread, fread, read_interval);
887          return 0;
888
889        case -EPIPE:
890          debug_msg("Got capture overrun XRUN (EPIPE)\n");
891          err = snd_pcm_prepare(current.rx.handle);
892          if (err<0) debug_msg("Failed snd_pcm_prepare from capture overrun");
893          err = snd_pcm_start(current.rx.handle);
894          if (err<0) debug_msg("Failed to re-start PCM capture from XRUN");
895          return FALSE;
896
897        case -ESTRPIPE:
898          debug_msg("Got capture ESTRPIPE\n");
899          while ((err = snd_pcm_resume(current.rx.handle)) == -EAGAIN)
900              sleep(1);       /* wait until the suspend flag is released */
901          if (err < 0) {
902              err = snd_pcm_prepare(current.rx.handle);
903              if (err<0) debug_msg("Failed snd_pcm_prepare from capture suspend");
904          }
905          return FALSE;
906
907        default:
908          debug_msg("Read failed status= %s\n", snd_strerror(fread));
909          return 0;
910    }
911}
912
913/*
914 * Playback audio data.
915 */
916
917
918int alsa_audio_write(audio_desc_t ad __attribute__((unused)),
919                     u_char *buf, int bytes)
920{
921    int fwritten, err, num_bytes=0 ,read_interval;
922    struct timeval          curr_time;
923    static struct timeval          last_read_time;
924    snd_pcm_sframes_t delay;
925    snd_pcm_sframes_t frames =
926        snd_pcm_bytes_to_frames(current.tx.handle,bytes);
927    snd_output_t *output = NULL;
928    snd_output_stdio_attach(&output, stdout, 0);
929
930    snd_pcm_status_t *status;
931    snd_pcm_status_alloca(&status);
932    err = snd_pcm_status(current.tx.handle, status);
933    if (err<0) {
934      debug_msg("Can't get status of tx");
935      return FALSE;
936    }
937
938    gettimeofday(&curr_time, NULL);
939    ///debug_msg("Audio write %d\n", bytes);
940    switch (snd_pcm_state(current.tx.handle)) {
941    case SND_PCM_STATE_RUNNING:
942      //debug_msg("In SND_PCM_STATE_RUNNING \n");
943      break;
944    case SND_PCM_STATE_XRUN:
945      debug_msg("In SND_PCM_STATE_XRUN  - preparing audio \n");
946        err = snd_pcm_prepare(current.tx.handle);
947      break;
948    default:
949      break;
950    }
951    read_interval = (curr_time.tv_sec  - last_read_time.tv_sec) * 1000000 + (curr_time.tv_usec - last_read_time.tv_usec);
952    snd_pcm_delay(current.tx.handle, &delay);
953
954    //debug_msg("Frames avail to be written=%d, time diff=%d, Trying to write %d frames, Curr delay %d \n",snd_pcm_avail_update(current.tx.handle), read_interval, frames, delay);
955    //snd_pcm_status_dump(status, output);
956    last_read_time.tv_sec=curr_time.tv_sec;
957    last_read_time.tv_usec=curr_time.tv_usec;
958
959    fwritten = snd_pcm_writei(current.tx.handle, buf, frames);
960    if (fwritten >= 0) {
961        // Normal case
962        num_bytes = snd_pcm_frames_to_bytes(current.tx.handle, fwritten);
963        //debug_msg("Wrote %d bytes, frames: %d\n", num_bytes, fwritten);
964        err = snd_pcm_status(current.tx.handle, status);
965        //snd_pcm_status_dump(status, output);
966        return num_bytes;
967    }
968    debug_msg("Err: Tried to Write %d bytes, frames: %d, but got: %d\n", bytes,frames, fwritten);
969
970    // Something happened
971    switch (fwritten)
972    {
973      case -EAGAIN:
974        // Normal when non-blocking
975        return 0;
976
977      case -EPIPE:
978        debug_msg("Got transmit underun XRUN(EPIPE) when trying to write audio\n");
979        err = snd_pcm_prepare(current.tx.handle);
980        if (err<0) debug_msg("Failed snd_pcm_prepare from Transmit underrun\n");
981        debug_msg("Attempting Recovery from transmit underrun: Bytes:%d Frames:%d\n",num_bytes,fwritten);
982        fwritten = snd_pcm_writei(current.tx.handle, buf, frames);
983        if (fwritten<0) {
984          debug_msg("Can't recover from transmit underrun\n");
985          return 0;
986        } else {
987          num_bytes = snd_pcm_frames_to_bytes(current.tx.handle, fwritten);
988          debug_msg("Recovered from transmit underrun: Bytes:%d Frames:%d\n",num_bytes,fwritten);
989          err = snd_pcm_status(current.tx.handle, status);
990          //snd_pcm_status_dump(status, output);
991          return num_bytes;
992        }
993
994      case -ESTRPIPE:
995        debug_msg("Got transmit ESTRPIPE\n");
996        while ((err = snd_pcm_resume(current.tx.handle)) == -EAGAIN)
997            sleep(1);       /* wait until the suspend flag is released */
998        if (err < 0) {
999            err = snd_pcm_prepare(current.tx.handle);
1000            if (err<0) debug_msg("Can't recovery from transmit suspend\n");
1001        }
1002        return 0;
1003
1004      default:
1005        //debug_msg("Write failed status=%d: %s\n", fwritten, snd_strerror(fwritten));
1006        return 0;
1007    }
1008
1009
1010    num_bytes = snd_pcm_frames_to_bytes(current.tx.handle, fwritten);
1011    debug_msg("Audio wrote %d\n", num_bytes);
1012    return num_bytes;
1013}
1014
1015
1016/*
1017 * Set options on audio device to be non-blocking.
1018 */
1019void alsa_audio_non_block(audio_desc_t ad __attribute__((unused)))
1020    {
1021    int err;
1022    debug_msg("Set nonblocking\n");
1023
1024    err = snd_pcm_nonblock(current.tx.handle, TRUE);
1025    if(err<0) debug_msg("Error setting TX non-blocking");
1026
1027    err = snd_pcm_nonblock(current.rx.handle, TRUE);
1028    if(err<0) debug_msg("Error setting RX non-blocking");
1029}
1030
1031/*
1032 * Set options on audio device to be blocking.
1033 */
1034void alsa_audio_block(audio_desc_t ad)
1035    {
1036    int err;
1037    debug_msg("[%d] set blocking\n", ad);
1038    if ((err = snd_pcm_nonblock(current.tx.handle, FALSE)) < 0) {
1039        debug_msg ("Cannot set device to be blocking: %s\n", snd_strerror(err));
1040    }
1041}
1042
1043
1044
1045/*
1046 * Output port controls.  In our case there is only one output port, the
1047 * PCM control (or Master if PCM fails), so this is a dummy.
1048 */
1049void
1050alsa_audio_oport_set(audio_desc_t ad, audio_port_t port)
1051{
1052    debug_msg("oport_set %d %d\n", ad, port);
1053}
1054
1055audio_port_t
1056alsa_audio_oport_get(audio_desc_t ad)
1057{
1058    debug_msg("oport_get %d\n", ad);
1059    return oport;
1060}
1061
1062int
1063alsa_audio_oport_count(audio_desc_t ad)
1064{
1065    debug_msg("Get oport count for %d (num=%d)\n", ad, NUM_OUT_PORTS);
1066    return NUM_OUT_PORTS;
1067}
1068
1069const audio_port_details_t*
1070alsa_audio_oport_details(audio_desc_t ad, int idx)
1071{
1072    debug_msg("oport details ad=%d idx=%d\n", ad, idx);
1073    if (idx >= 0 && idx < NUM_OUT_PORTS) {
1074        return &out_ports[idx];
1075    }
1076    return NULL;
1077}
1078
1079/*
1080 * Set input port.
1081 */
1082void
1083alsa_audio_iport_set(audio_desc_t ad, audio_port_t port)
1084{
1085    int err = 0;
1086    audio_port_t i=port;
1087    snd_mixer_elem_t *selem;
1088
1089    ad=ad; // Silence compiler warning
1090    current.iport = port;
1091    //The following does not seems necessary - though it does work
1092    //selem = snd_mixer_find_selem(current.mixer, iports[port].sid);
1093    selem = iports[port].smixer_elem;
1094    debug_msg("Selecting input port: %s\n",iports[i].details.name);
1095
1096    if(selem)
1097      if (snd_mixer_selem_has_capture_switch(selem)) {
1098        if (snd_mixer_selem_has_capture_switch_joined(selem)){
1099          snd_mixer_selem_get_capture_switch(selem,SND_MIXER_SCHN_FRONT_LEFT,&err);
1100          debug_msg("Found snd_mixer_selem_has_capture_switch_joined\n");
1101          err=snd_mixer_selem_set_capture_switch_all(selem,1);
1102          if (err<0) {
1103            debug_msg("Error encountered setting joined capture switch\n" );
1104          }
1105        } else  {
1106          debug_msg("Found separate L+R captures levels\n" );
1107          err=snd_mixer_selem_set_capture_switch(selem,SND_MIXER_SCHN_FRONT_LEFT,1);
1108          if (err<0) {
1109            debug_msg("Error setting Left capture switch \n" );
1110          }
1111          err=snd_mixer_selem_set_capture_switch(selem,SND_MIXER_SCHN_FRONT_RIGHT,1);
1112          if (err<0) {
1113            debug_msg("Error setting Right capture switch \n" );
1114          }
1115        }
1116    }
1117    // Make sure Capture switch is on.
1118    if (current.rxgain) {
1119      if (snd_mixer_selem_has_capture_switch(current.rxgain)) {
1120        err=snd_mixer_selem_set_capture_switch_all(current.rxgain,1);
1121        if (err<0) {
1122          debug_msg("Error setting Capture switch\n" );
1123        }
1124      }
1125    }
1126handle_mixer_events(current.mixer);
1127//    mixer_state_change = 1;
1128}
1129
1130
1131
1132/*
1133 * Get input port.
1134 */
1135audio_port_t
1136alsa_audio_iport_get(audio_desc_t ad)
1137{
1138    debug_msg("iport_get %d\n", ad);
1139    return current.iport;
1140}
1141
1142int
1143alsa_audio_iport_count(audio_desc_t ad)
1144{
1145    debug_msg("Get iport count for %d (num=%d)\n", ad, num_iports);
1146    return num_iports;
1147}
1148
1149const audio_port_details_t* alsa_audio_iport_details(audio_desc_t ad, int idx)
1150{
1151    int err;
1152    snd_mixer_selem_get_capture_switch(iports[idx].smixer_elem,SND_MIXER_SCHN_FRONT_LEFT,&err);
1153    debug_msg("iport details ad=%d idx=%d : %s  (Enabled=%d)\n", ad, idx, iports[idx].details.name,err);
1154    return &iports[idx].details;
1155}
1156
1157
1158/*
1159 * For external purposes this function returns non-zero
1160 * if audio is ready.
1161 */
1162int alsa_audio_is_ready(audio_desc_t ad __attribute__((unused)))
1163{
1164    snd_pcm_status_t *status;
1165    snd_pcm_uframes_t avail;
1166    int err;
1167    snd_pcm_sframes_t frames;
1168    snd_output_t *output = NULL;
1169
1170    snd_output_stdio_attach(&output, stdout, 0);
1171    snd_pcm_status_alloca(&status);
1172
1173    err = snd_pcm_status(current.rx.handle, status);
1174    if (err<0) {
1175      debug_msg("Can't get status of rx");
1176      return FALSE;
1177    }
1178
1179    avail = snd_pcm_frames_to_bytes(current.rx.handle, //frames
1180                                    snd_pcm_status_get_avail(status));
1181
1182    if (!snd_pcm_status_get_avail_max(status)) {
1183      debug_msg("Resetting audio as statusmaxframes is zero\n");
1184      err = snd_pcm_prepare(current.rx.handle);
1185      if (err<0) debug_msg("Failed snd_pcm_prepare in audio_ready");
1186      err = snd_pcm_start(current.rx.handle);
1187      if (err<0) debug_msg("Failed to re-start in audio_ready");
1188      //dump status
1189      //snd_pcm_status_dump(status, output);
1190      //frames= snd_pcm_avail_update(current.rx.handle);
1191      debug_msg("audio_is_ready: snd_pcm_avail_update(current.rx.handle);: %d\n",  snd_pcm_avail_update(current.rx.handle));
1192      debug_msg("audio_is_ready: %d bytes (bytes per blk %d)\n", avail, current.bytes_per_block);
1193    }
1194 
1195    return (avail >= current.bytes_per_block);
1196}
1197
1198
1199void alsa_audio_wait_for(audio_desc_t ad __attribute__((unused)), int delay_ms)
1200{
1201    ///debug_msg("Audio wait %d\n", delay_ms);
1202    snd_pcm_wait(current.rx.handle, delay_ms);
1203}
1204
1205
1206char* alsa_get_device_name(audio_desc_t idx)
1207{
1208    debug_msg("Get name for card %d: \"%s\"\n", idx, ratCards[idx].name);
1209    return ratCards[idx].name;
1210}
1211
1212
1213int alsa_audio_init()
1214{
1215    int fd;
1216    char buf[4096];
1217    char *version;
1218    size_t count;
1219    int result =  FALSE;
1220
1221    // Based on xmms-alsa
1222
1223    fd = open("/proc/asound/version", O_RDONLY, 0);
1224    if (fd < 0) {
1225        result = FALSE;
1226        } 
1227
1228    count = read(fd, buf, sizeof(buf) - 1);
1229    buf[count] = 0;
1230    close(fd);
1231
1232    debug_msg("ALSA version identifier == %s\n", buf);
1233
1234    version = strstr(buf, " Version ");
1235
1236    if (version == NULL) {
1237        result = FALSE;
1238    }
1239           
1240    version += 9; /* strlen(" Version ") */
1241
1242    /* The successor to 0.9 might be 0.10, not 1.0.... */
1243    if (strcmp(version, "0.9") > 0  ||  isdigit(version[3])) {
1244        result = TRUE;
1245    } else {
1246        result = FALSE;
1247            }
1248
1249    debug_msg("ALSA init result == %d\n", result);
1250
1251    clear_current();
1252    return result;
1253}
1254
1255int alsa_get_device_count()
1256{
1257    snd_ctl_t *ctl_handle;
1258    snd_ctl_card_info_t *ctl_info;
1259    int err, cindex = 0, alsaDevNo=0;
1260    char card[128];
1261    // Ptr to ratCards array
1262    RatCardInfo *ratCard;
1263
1264    debug_msg("ALSA get device count\n");
1265
1266    snd_ctl_card_info_alloca(&ctl_info);
1267   
1268    // Get ALSA "default" card - so things like dsnoop etc can work
1269    if (!snd_ctl_open(&ctl_handle, "default", SND_CTL_NONBLOCK)) {
1270      if ((err = snd_ctl_card_info (ctl_handle, ctl_info) < 0)) {
1271        debug_msg("ALSA default card query failed: %s\n", snd_strerror(err));
1272        snd_ctl_close(ctl_handle);
1273      } else {
1274        ratCard = ratCards + cindex;
1275        ratCard->card_number = cindex;
1276        ratCard->pcm_device = strdup("default");
1277        ratCard->pcm_mixer = strdup("default");
1278        snprintf(card, sizeof(card), "ALSA default: %s",
1279                                  snd_ctl_card_info_get_name (ctl_info));
1280        ratCard->name = strdup (card);
1281        debug_msg("Got \"default\" card %s\n", ratCard->name);
1282        snd_ctl_close(ctl_handle);
1283        cindex++;
1284      }
1285    }
1286
1287    do {
1288        sprintf (card , "hw:%d", alsaDevNo);
1289        err = snd_ctl_open (&ctl_handle, card, SND_CTL_NONBLOCK);
1290
1291        if (err == 0) {
1292            debug_msg("Opened ALSA dev:%s\n",card);
1293            // Grab the card info
1294            ratCard = ratCards + cindex;
1295            ratCard->card_number = cindex;
1296            ratCard->pcm_mixer = strdup(card);
1297            sprintf (card , "plughw:%d", alsaDevNo);
1298            ratCard->pcm_device = strdup(card);
1299
1300            if ((err = snd_ctl_card_info (ctl_handle, ctl_info) < 0)) {
1301                snprintf(card, sizeof(card), "ALSA %d: Not Available", cindex);
1302                ratCard->name = strdup (card);
1303                debug_msg("Got failed ALSA card %s (err:%s)\n", ratCard->name,snd_strerror(err));
1304                err=0;
1305            } else {
1306                snprintf(card, sizeof(card), "ALSA %d: %s", cindex,
1307                         snd_ctl_card_info_get_name (ctl_info));
1308                ratCard->name = strdup (card);
1309                debug_msg("Got card %s\n", ratCard->name);
1310            }
1311            snd_ctl_close(ctl_handle);
1312           
1313        }
1314        cindex++;
1315        alsaDevNo++;
1316
1317    } while (err == 0);
1318
1319    nRatCards = cindex - 1;
1320    debug_msg("Got %d ALSA devices\n", nRatCards);
1321
1322    return nRatCards;
1323}
1324
1325int alsa_audio_supports(audio_desc_t ad, audio_format *fmt)
1326{
1327    snd_pcm_hw_params_t *hw_params;
1328    unsigned rmin, rmax, cmin, cmax;
1329    int err, dir;
1330
1331    debug_msg("ALSA support  %d\n", ad);
1332    dump_audio_format(fmt);
1333
1334    snd_pcm_hw_params_alloca (&hw_params);
1335    err = snd_pcm_hw_params_any (current.tx.handle, hw_params);
1336
1337    err = snd_pcm_hw_params_get_rate_min (hw_params, &rmin, &dir);
1338    CHECKERR("Failed to get min rate");
1339    err = snd_pcm_hw_params_get_rate_max (hw_params, &rmax, &dir);
1340    CHECKERR("Failed to get max rate");
1341
1342    err = snd_pcm_hw_params_get_channels_min (hw_params, &cmin);
1343    CHECKERR("Failed to get min channels");
1344    err = snd_pcm_hw_params_get_channels_max (hw_params, &cmax);
1345    CHECKERR("Failed to get max channels");
1346
1347    if ((fmt->sample_rate >= rmin) && (fmt->sample_rate <= rmax) &&
1348        (fmt->channels >= (int)cmin) && (fmt->channels <= (int)cmax))
1349{
1350        debug_msg("Config is supported\n");
1351        return TRUE;
1352    }
1353    return FALSE;
1354}
Note: See TracBrowser for help on using the browser.