Logo Search packages:      
Sourcecode: tucnak2 version File versions  Download package

alsa.c

/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2006  Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or                                                        
    modify it under the terms of the GNU General Public License                                                          
    version 2 as published by the Free Software Foundation.

*/

#include "header.h"
#ifdef HAVE_ALSA


void device_info(int card, int pcm_device, snd_pcm_stream_t dir){
    int err;
    char pcm_name[256], mixer_name[256];
    snd_pcm_hw_params_t *hwparams;
    snd_pcm_t *pcm;
    snd_mixer_t *mixer_handle;
    snd_mixer_elem_t *elem;
    snd_ctl_t *ctl_handle;
    snd_ctl_card_info_t *hw_info;
    int sw;
    const char *mixer_chip;

    sprintf(pcm_name, "hw:%d,%d", card, pcm_device);

    err=snd_pcm_open(&pcm, pcm_name, dir, 0);
    if (err<0) return;

    snd_pcm_hw_params_alloca(&hwparams);
    snd_ctl_card_info_alloca(&hw_info);

    err=snd_pcm_hw_params_any(pcm, hwparams);
    if (err<0) goto x;

/*    int rate_num,rate_den;
    err=snd_pcm_hw_params_get_rate_numden(hwparams, &rate_num, &rate_den);
    if (err<0) goto x;
    printf("        rate_num=%d rate_den=%d\n", rate_num, rate_den);
  */

    unsigned int channels_min, channels, channels_max;
    err=snd_pcm_hw_params_get_channels_min(hwparams, &channels_min);
    if (err<0) goto x1;
    err=snd_pcm_hw_params_get_channels(hwparams, &channels);
    if (err<0) goto x1;
    err=snd_pcm_hw_params_get_channels_max(hwparams, &channels_max);
    if (err<0) goto x1;
    printf("        channels=%d .. %d .. %d\n", channels_min, channels, channels_max);
x1:;
    
    unsigned int rate_min, rate, rate_max;
    int dir_min, dir_, dir_max;
    err=snd_pcm_hw_params_get_rate_min(hwparams, &rate_min, &dir_min);
    if (err<0) goto x2;
    err=snd_pcm_hw_params_get_rate(hwparams, &rate, &dir_);
    if (err<0) goto x2;
    err=snd_pcm_hw_params_get_rate_max(hwparams, &rate_max, &dir_max);
    if (err<0) goto x2;
    printf("        rate=%d .. %d .. %d\n", rate_min, rate, rate_max);
x2:

    if (dir == SND_PCM_STREAM_CAPTURE){
        sprintf(mixer_name, "hw:%d", card);

        snd_ctl_open(&ctl_handle, mixer_name, 0);
        if (err<0) goto x3c;
        err = snd_ctl_card_info(ctl_handle, hw_info);
        if (err<0){
            snd_ctl_close(ctl_handle);
            goto x3c;
        }
        mixer_chip=snd_ctl_card_info_get_mixername(hw_info);
        printf("        mixer chip=%s\n", mixer_chip);
        snd_ctl_close(ctl_handle);

x3c:        
        err=snd_mixer_open(&mixer_handle, 0);
        if (err<0) goto x3;
        err=snd_mixer_attach(mixer_handle, mixer_name);
        if (err<0) goto x3;
        err=snd_mixer_selem_register(mixer_handle, NULL, NULL);
        if (err<0) goto x3;
        err=snd_mixer_load(mixer_handle);
        if (err<0) goto x3;

        printf("        elements=");
        for (elem=snd_mixer_first_elem(mixer_handle);
             elem;
             elem=snd_mixer_elem_next(elem)){

            if (!snd_mixer_selem_has_capture_switch(elem)) continue;
                  
            err=snd_mixer_selem_get_capture_switch(elem, 0, &sw);
            if (err<0) goto x3;
            
            if (sw)
                printf("[%s] ", snd_mixer_selem_get_name(elem));
            else
                printf("'%s' ", snd_mixer_selem_get_name(elem));
        }
        printf("\n");

x3:
        snd_mixer_close(mixer_handle);
        
    }
    
x:
    snd_pcm_close(pcm);
    
}


void card_info(int card){
    int pcm_device;
    char *name;
    snd_ctl_t *ctl;
    char dev[256];
    snd_pcm_info_t *pcm_info;

    printf("card hw:%d ",card);

    if (snd_card_get_name(card, &name)==0){
        printf("%s", name);
    }
    printf("\n");                

    sprintf(dev,"hw:%d", card);
    snd_ctl_open(&ctl, dev, 0);
    
    snd_pcm_info_alloca(&pcm_info);    

    pcm_device=-1;
    while(1){
        if (snd_ctl_pcm_next_device(ctl, &pcm_device)) pcm_device=-1;
        if (pcm_device<0) break;

        snd_pcm_info_set_device(pcm_info, pcm_device);
        snd_pcm_info_set_subdevice(pcm_info, 0);
        snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_PLAYBACK);
        
        if (snd_ctl_pcm_info(ctl, pcm_info)<0) continue;
        
        printf("    playback hw:%d,%d %s\n", card, pcm_device, snd_pcm_info_get_name(pcm_info)); 
        device_info(card, pcm_device, SND_PCM_STREAM_PLAYBACK);
    }
    
    pcm_device=-1;
    while(1){
        if (snd_ctl_pcm_next_device(ctl, &pcm_device)) pcm_device=-1;
        if (pcm_device<0) break;

        snd_pcm_info_set_device(pcm_info, pcm_device);
        snd_pcm_info_set_subdevice(pcm_info, 0);
        snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_CAPTURE);
        
        if (snd_ctl_pcm_info(ctl, pcm_info)<0) continue;
        
        printf("    capture  hw:%d,%d %s\n", card, pcm_device, snd_pcm_info_get_name(pcm_info)); 
        device_info(card, pcm_device, SND_PCM_STREAM_CAPTURE);
    }
}

void alsa_info(){
    int card;
    
    printf("\n  alsa_info:\n");
    card=-1;
    snd_card_next(&card);
    while(card>-1){
        card_info(card);
        snd_card_next(&card);
    }
    
    printf("\n");
}



int alsa_open(struct dsp *dsp, int rec){
    int err, ratedir;
    unsigned int rateval;
    snd_pcm_hw_params_t *hwparams;
    snd_pcm_sw_params_t *swparams;
    /*snd_pcm_uframes_t frames;*/
    unsigned buffer_time, period_time;
    int dir;
    snd_pcm_uframes_t buffer_size, period_size;


    if (dsp->pcm_opened) alsa_close(dsp);

    CONDGFREE(dsp->name);
    if (rec){
        dsp->name=g_strdup(cfg->ssbd_pcm_rec);
    }else{
        dsp->name=g_strdup(cfg->ssbd_pcm_play);
    }
    
    dbg("alsa_open('%s', %s)\n", dsp->name, rec?"capture":"play");

    err=snd_pcm_open(&dsp->pcm, dsp->name, rec?SND_PCM_STREAM_CAPTURE:SND_PCM_STREAM_PLAYBACK, 0);
    if (err<0){
        log_addf("Can't open alsa PCM '%s': %s", dsp->name, snd_strerror(-err));
        goto err;
    }
    dbg("alsa opened (%s)\n", dsp->name);
    dsp->pcm_opened=1;
    
/*    err=snd_pcm_nonblock(dsp->pcm, 1);
    if (err<0){
        log_addf("alsa_open: snd_pcm_nonblock failed: %s", snd_strerror(-err));
        goto err;
    }  */
    
    snd_pcm_hw_params_alloca(&hwparams);
    snd_pcm_sw_params_alloca(&swparams);

    
    /* HW parameters */
    err=snd_pcm_hw_params_any(dsp->pcm, hwparams);
    if (err<0){
        log_addf("alsa_open: snd_pcm_hw_params_any failed: %s", snd_strerror(-err));
        goto err;
    }  
    /* read/write format */
    err=snd_pcm_hw_params_set_access(dsp->pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err<0){
        log_addf("alsa_open: snd_pcm_hw_params_set_access failed: %s", snd_strerror(-err));
        goto err;
    }  
    
    /* sample format */
    err=snd_pcm_hw_params_set_format(dsp->pcm, hwparams, dsp->pcm_format);
    if (err<0){
        log_addf("alsa_open: snd_pcm_hw_params_set_format failed: %s", snd_strerror(-err));
        goto err;
    }  
    
    /* channels */
    err=snd_pcm_hw_params_set_channels(dsp->pcm, hwparams, dsp->channels);
    if (err<0){
        log_addf("alsa_open: snd_pcm_hw_params_set_channels failed: %s", snd_strerror(-err));
        goto err;
    }  
    
    /* sample rate */
    rateval=dsp->speed;
    ratedir=0;
    err=snd_pcm_hw_params_set_rate_near(dsp->pcm, hwparams, &rateval, &ratedir);
    if (err<0){
        log_addf("alsa_open: snd_pcm_hw_params_set_rate_near failed: %s", snd_strerror(-err));
        goto err;
    }  
#if 0    
    /* period size */
    frames=4096;
    err=snd_pcm_hw_params_set_period_size_near(dsp->pcm, hwparams, &frames, 0);
    if (err<0){
        log_addf("alsa_open: snd_pcm_hw_params_set_period_size_near failed: %s", snd_strerror(-err));
        goto err;
    }  

    /* buffer size */
    frames=1024;
    err=snd_pcm_hw_params_set_buffer_size_near(dsp->pcm, hwparams, &frames);
    if (err<0){
        log_addf("alsa_open: snd_pcm_hw_params_set_period_size_near failed: %s", snd_strerror(-err));
        goto err;
    }  

    /* periods count */
    /*periods=4;
    err=snd_pcm_hw_params_set_periods_near(dsp->pcm, hwparams, &periods, 0);
    if (err<0){
        log_addf("alsa_open: snd_pcm_hw_params_set_periods_near failed: %s", snd_strerror(-err));
        goto err;
    } */ 
#endif

       /* set the buffer time */
    dir = 1; /* not sure if dir is input parameter. */
    buffer_time=500000;
    period_time=100000;
    if (cfg->ssbd_buffer_time>0) buffer_time=cfg->ssbd_buffer_time;
    if (cfg->ssbd_period_time>0) period_time=cfg->ssbd_period_time;

      dbg("snd_pcm_hw_params_set_buffer_time_near(buffer_time=%d, dir=%d)\n", buffer_time, dir);
      err = snd_pcm_hw_params_set_buffer_time_near(dsp->pcm, hwparams, &buffer_time, &dir);
      if (err < 0) {
            log_addf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
            goto err;
      }
      err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
      if (err < 0) {
            log_addf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
            goto err;
      }
      dbg("snd_pcm_hw_params_get_buffer_size()=%d\n", buffer_size);
      /* set the period time */
      dbg("snd_pcm_hw_params_set_period_time_near(period_time=%d, dir=%d\n", period_time, dir);
      err = snd_pcm_hw_params_set_period_time_near(dsp->pcm, hwparams, &period_time, &dir);
      if (err < 0) {
            log_addf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
            goto err;
      }
      err = snd_pcm_hw_params_get_period_size(hwparams, &period_size, &dir);
      if (err < 0) {
            log_addf("Unable to get period size for playback: %s\n", snd_strerror(err));
            goto err;
      }
      dbg("snd_pcm_hw_params_get_period_size() period_size=%d dir=%d\n", period_size, dir);

 
    /* write hwparams */
    err=snd_pcm_hw_params(dsp->pcm, hwparams);
    if (err<0){
        log_addf("alsa_open: snd_pcm_hw_params failed: %s", snd_strerror(-err));
        goto err;
    }  

#if 0
      /* get the current swparams */
      err = snd_pcm_sw_params_current(dsp->pcm, swparams);
      if (err < 0) {
            printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
            goto err;
      }
      /* start the transfer when the buffer is almost full: */
      /* (buffer_size / avail_min) * avail_min */
      err = snd_pcm_sw_params_set_start_threshold(dsp->pcm, swparams, (buffer_size / period_size) * period_size);
      if (err < 0) {
            printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
            goto err;
      }
      /* allow the transfer when at least period_size samples can be processed */
      err = snd_pcm_sw_params_set_avail_min(dsp->pcm, swparams, period_size);
      if (err < 0) {
            printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
            goto err;
      }
      /* align all transfers to 1 sample */
      err = snd_pcm_sw_params_set_xfer_align(dsp->pcm, swparams, 1);
      if (err < 0) {
            printf("Unable to set transfer align for playback: %s\n", snd_strerror(err));
            goto err;
      }
      /* write the parameters to the playback device */
      err = snd_pcm_sw_params(dsp->pcm, swparams);
      if (err < 0) {
            printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
            goto err;
      }                  
#endif    
    
    goto x;

err:;
    dbg("alsa not opened\n");
    alsa_close(dsp);
    return -1;
x:;
    dbg("alsa opened\n");
    return 0;
}


int alsa_close(struct dsp *dsp){
    if (!dsp->pcm_opened) return 0;
    snd_pcm_close(dsp->pcm);
    dbg("alsa closed (%s)\n", dsp->name);
    dsp->pcm_opened=0;
    return 0;
}


int alsa_write(struct dsp *dsp, void *data, int len){
    int frames;
    
    /*dbg("alsa_write(len=%d)\n", len);*/
    frames=snd_pcm_writei(dsp->pcm, data, len/(dsp->bpf*dsp->channels));
    if (frames<0) {
        dbg("snd_pcm_writei returns %d %s, trying to recover...\n", frames, snd_strerror(-frames));
        if (frames==-EPIPE) frames=snd_pcm_prepare(dsp->pcm);    /* snd_pcm_recover is since libasound 1.0.11 */
    }
    if (frames>0) frames*=dsp->bpf*dsp->channels;
    return frames;
}


int alsa_read(struct dsp *dsp, void *data, int len){
    int frames;
    
    frames=snd_pcm_readi(dsp->pcm, data, len/(dsp->bpf*dsp->channels));
    if (frames<0) {
        dbg("snd_pcm_readi returns %d, trying to recover...\n", frames);
        if (frames==-EINTR) frames=snd_pcm_prepare(dsp->pcm);
    }
    if (frames>0) frames*=dsp->bpf*dsp->channels;
    return frames;
}


int alsa_reset(struct dsp *dsp){
    snd_pcm_drop(dsp->pcm);
    return 0;
}


int alsa_sync(struct dsp *dsp){
    snd_pcm_drain(dsp->pcm);
    return 0;
}

int alsa_set_format(struct dsp *dsp, SF_INFO *sfinfo){

    dsp->bpf=1;
    switch (sfinfo->format & SF_FORMAT_SUBMASK){
/*        case SF_FORMAT_PCM_U8:
            format=AFMT_U8;
            break;
        case SF_FORMAT_PCM_S8:
            format=AFMT_S8;
            break;*/
        case SF_FORMAT_ULAW:
            dsp->pcm_format=SND_PCM_FORMAT_MU_LAW;
            break;            
        case SF_FORMAT_ALAW:
            dsp->pcm_format=SND_PCM_FORMAT_A_LAW;
            break;            
        case SF_FORMAT_IMA_ADPCM:
            dsp->pcm_format=SND_PCM_FORMAT_IMA_ADPCM;
            break;            
        default:
            dsp->pcm_format=SND_PCM_FORMAT_S16_LE;    
            dsp->bpf=2;
    }
    dsp->channels=sfinfo->channels; 
    dsp->speed=sfinfo->samplerate;   
    return 0;
}

int alsa_get_sources(GString *labels){
    int err;
    snd_mixer_t *mixer_handle;
    snd_mixer_elem_t *elem;
    const char *label;

    g_string_truncate(labels, 0);
    if (!cfg->ssbd_alsa_mixer || strlen(cfg->ssbd_alsa_mixer)==0) return 0; 

    err=snd_mixer_open(&mixer_handle, 0);
    if (err<0) goto x3;
    err=snd_mixer_attach(mixer_handle, cfg->ssbd_alsa_mixer);
    if (err<0) goto x3;
    err=snd_mixer_selem_register(mixer_handle, NULL, NULL);
    if (err<0) goto x3;
    err=snd_mixer_load(mixer_handle);
    if (err<0) goto x3;

    for (elem=snd_mixer_first_elem(mixer_handle);
         elem;
         elem=snd_mixer_elem_next(elem)){

        if (!snd_mixer_selem_has_capture_switch(elem)) continue;
              
        label=snd_mixer_selem_get_name(elem);
        if (strcmp(label, "Capture")==0) continue;

        if (strlen(labels->str)>0) g_string_append_c(labels, ';');
        g_string_append(labels, label);
    }
x3:;    
    snd_mixer_close(mixer_handle);
    return 0;
}

int alsa_set_source(struct dsp *dsp){
    int err;
    snd_mixer_t *mixer_handle;
    snd_mixer_elem_t *elem;
    const char *label;

    dbg("alsa_set_source('%s')\n", dsp->source);
    if (!dsp->source || !*dsp->source){
        return 0;
    }

    err=snd_mixer_open(&mixer_handle, 0);
    if (err<0) goto x3;
    err=snd_mixer_attach(mixer_handle, cfg->ssbd_alsa_mixer);
    if (err<0) goto x3;
    err=snd_mixer_selem_register(mixer_handle, NULL, NULL);
    if (err<0) goto x3;
    err=snd_mixer_load(mixer_handle);
    if (err<0) goto x3;

    for (elem=snd_mixer_first_elem(mixer_handle);
         elem;
         elem=snd_mixer_elem_next(elem)){

        if (!snd_mixer_selem_has_capture_switch(elem)) continue;
        label=snd_mixer_selem_get_name(elem);
        if (strcmp(label, dsp->source)==0){
          snd_mixer_selem_set_capture_switch_all(elem, 1);
            dbg("alsa_set_source('%s') SET\n", dsp->source);
        }
        if (strcmp(label, "Capture")==0){
          snd_mixer_selem_set_capture_switch_all(elem, 1);
            dbg("alsa_set_source('Capture') SET\n");
        }
    }
x3:;    
    snd_mixer_close(mixer_handle);
    return 0;
}

#endif

Generated by  Doxygen 1.6.0   Back to index