Пример #1
0
static gint
pcm_open_and_load_hwparams(alsa_driver *d)
{
    gint err;

    if((err = snd_pcm_open(&(d->soundfd), d->device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
	alsa_error(N_("ALSA device opening error"), err);
	return -1;
    }
    if((err = snd_pcm_hw_params_any(d->soundfd, d->hwparams)) < 0) {
	alsa_error(N_("hw params obtaining error"), err);
	snd_pcm_close(d->soundfd);
	return -1;
    }
    return 0;
}
Пример #2
0
void scan_cycle(alsa_rawmidi_t *midi)
{
    int card = -1, err;
    scan_t scan;
    midi_port_t **ports;

    //debug_log("scan: cleanup");
    scan_cleanup(midi);

    scan.midi = midi;
    scan.iterator = &midi->scan.ports;
    snd_rawmidi_info_alloca(&scan.info);

    //debug_log("scan: rescan");
    while ((err = snd_card_next(&card))>=0 && card>=0) {
        char name[32];
        snprintf(name, sizeof(name), "hw:%d", card);
        if ((err = snd_ctl_open(&scan.ctl, name, SND_CTL_NONBLOCK))>=0) {
            scan_card(&scan);
            snd_ctl_close(scan.ctl);
        } else
            alsa_error("scan: snd_ctl_open", err);
    }

    // delayed open to workaround alsa<1.0.14 bug (can't open more than 1 subdevice if ctl is opened).
    ports = &midi->scan.ports;
    while (*ports) {
        midi_port_t *port = *ports;
        if (port->state == PORT_CREATED)
            ports = scan_port_open(midi, ports);
        else
            ports = &port->next;
    }
}
Пример #3
0
void alsa_clear_config_cache(void)
{
    int r;

    r = snd_config_update_free_global();
    if (r < 0)
        alsa_error("config_update_free_global", r);
}
Пример #4
0
static bool chk(const char *s, int r)
{
    if (r < 0) {
        alsa_error(s, r);
        return false;
    } else {
        return true;
    }
}
Пример #5
0
static int pcm_revents(struct alsa_pcm *alsa, unsigned short *revents) {
    int r;

    r = snd_pcm_poll_descriptors_revents(alsa->pcm, alsa->pe, alsa->pe_count,
                                         revents);
    if (r < 0) {
        alsa_error("poll_descriptors_revents", r);
        return -1;
    }
    
    return 0;
}
Пример #6
0
static void *
alsa_new (void)
{
    guint i;
    gint err;
    alsa_driver *d = g_new(alsa_driver, 1);

    d->device = g_strdup("hw:0,0");
    d->bits = 8;
    d->stereo = 0;
    d->buffer_size = 14;
    d->playrate = 44100;
    d->minfreq_old = 0;
    d->maxfreq_old = 0;
    d->address_old = 0;
    d->bufsize_old = 0;
    d->num_periods = 1;
    d->can8 = TRUE;
    d->can16 = TRUE;
    d->canmono = TRUE;
    d->canstereo = TRUE;
    d->signedness8 = FALSE;
    d->signedness16 = TRUE;
    d->persizemin = 256;
    d->persizemax = 8192;

    for(i = 0; i < NUM_FORMATS; i++) {
	d->devcap[i].minfreq = 22050;
	d->devcap[i].maxfreq = 44100;
	d->devcap[i].minbufsize = 512;
	d->devcap[i].maxbufsize = 16384;
    }

    d->soundfd = 0;
    d->sndbuf = NULL;
    d->polltag = NULL;
    d->pfd = NULL;

    d->verbose = FALSE;
    d->hwtest = TRUE;

    if((err = snd_output_stdio_attach(&(d->output), stdout,0)) < 0) {
	alsa_error(N_("Error attaching sound output"), err);
	return NULL;
    }

    snd_pcm_hw_params_malloc(&(d->hwparams));
    snd_pcm_sw_params_malloc(&(d->swparams));
    alsa_make_config_widgets(d);

    return d;
}
Пример #7
0
static int pcm_close(struct alsa_pcm_t *alsa)
{
    int r;

    r = snd_pcm_close(alsa->pcm);
    if (r < 0) {
        alsa_error("close", r);
        return -1;
    }
    free(alsa->buf);

    return 0;
}
Пример #8
0
static int start(struct device_t *dv)
{
    int r;
    struct alsa_t *alsa = (struct alsa_t*)dv->local;

    r = snd_pcm_start(alsa->capture.pcm);
    if (r < 0) {
        alsa_error("start", r);
        return -1;
    }

    return 0;
}
Пример #9
0
static gint
set_rates (alsa_driver *d, guint channels, guint format)
{
    guint ratemin, ratemax;
    snd_pcm_uframes_t bufsizemin, bufsizemax;
    gint err;

    if((err = snd_pcm_hw_params_set_channels(d->soundfd, d->hwparams, channels)) < 0) {
	alsa_error(N_("Unable to set channles number"), err);
	return -1;
    }
    if((err = snd_pcm_hw_params_get_rate_min(d->hwparams, &ratemin, 0)) < 0) {
	alsa_error(N_("Unable to get minimal sample rate"), err);
	return -1;
    }
    d->devcap[format].minfreq = MAX(ratemin, 8000);

    if((err = snd_pcm_hw_params_get_rate_max(d->hwparams, &ratemax, 0)) < 0) {
	alsa_error(N_("Unable to get maximal sample rate"), err);
	return -1;
    }
    d->devcap[format].maxfreq = MIN(ratemax, 96000);

    if((err = snd_pcm_hw_params_get_buffer_size_min(d->hwparams, &bufsizemin)) < 0) {
	alsa_error(N_("Unable to get minimal buffer size"), err);
	return -1;
    }
    d->devcap[format].minbufsize = MAX(bufsizemin, 256);

    if((err = snd_pcm_hw_params_get_buffer_size_max(d->hwparams, &bufsizemax)) < 0) {
	alsa_error(N_("Unable to get maximal buffer size"), err);
	return -1;
    }
    d->devcap[format].maxbufsize = bufsizemax;

    return 0;
}
Пример #10
0
static
void scan_card(scan_t *scan)
{
    int device = -1;
    int err;

    while ((err = snd_ctl_rawmidi_next_device(scan->ctl, &device))>=0 && device >=0) {
        snd_rawmidi_info_set_device(scan->info, device);

        snd_rawmidi_info_set_stream(scan->info, SND_RAWMIDI_STREAM_INPUT);
        snd_rawmidi_info_set_subdevice(scan->info, 0);
        if ((err = snd_ctl_rawmidi_info(scan->ctl, scan->info))>=0)
            scan_device(scan);
        else if (err != -ENOENT)
            alsa_error("scan: snd_ctl_rawmidi_info on device", err);

        snd_rawmidi_info_set_stream(scan->info, SND_RAWMIDI_STREAM_OUTPUT);
        snd_rawmidi_info_set_subdevice(scan->info, 0);
        if ((err = snd_ctl_rawmidi_info(scan->ctl, scan->info))>=0)
            scan_device(scan);
        else if (err != -ENOENT)
            alsa_error("scan: snd_ctl_rawmidi_info on device", err);
    }
}
Пример #11
0
static
void scan_device(scan_t *scan)
{
    int err;
    int sub, nsubs = 0;
    nsubs = snd_rawmidi_info_get_subdevices_count(scan->info);

    for (sub=0; sub<nsubs; ++sub) {
        snd_rawmidi_info_set_subdevice(scan->info, sub);
        if ((err = snd_ctl_rawmidi_info(scan->ctl, scan->info)) < 0) {
            alsa_error("scan: snd_ctl_rawmidi_info on subdevice", err);
            continue;
        }

        scan_port_update(scan);
    }
}
Пример #12
0
static ssize_t pcm_pollfds(struct alsa_pcm *alsa, struct pollfd *pe,
			   size_t z)
{
    int r, count;

    count = snd_pcm_poll_descriptors_count(alsa->pcm);
    if (count > z)
        return -1;

    if (count == 0)
        alsa->pe = NULL;
    else {
        r = snd_pcm_poll_descriptors(alsa->pcm, pe, count);
        if (r < 0) {
            alsa_error("poll_descriptors", r);
            return -1;
        }
        alsa->pe = pe;
    }

    alsa->pe_count = count;
    return count;
}
Пример #13
0
static int alsa_set_hwparams()
{
	snd_pcm_hw_params_t *hwp;
	snd_pcm_sw_params_t *swp;
	int dir = 1;
	unsigned period_time;
	snd_pcm_uframes_t buffer_size, period_size;

	snd_pcm_hw_params_alloca(&hwp);
	snd_pcm_sw_params_alloca(&swp);

	// ALSA bug? If we request 44100 Hz, it rounds the value up to 48000...
	alsa_hw.rate--;

	if (alsa_error("hw_params_any", snd_pcm_hw_params_any(alsa_hw.handle, hwp))
	    || alsa_error("hw_params_set_format", snd_pcm_hw_params_set_format(alsa_hw.handle, hwp, alsa_hw.format))
	    || alsa_error("hw_params_set_channels",
			  snd_pcm_hw_params_set_channels(alsa_hw.handle, hwp, alsa_hw.num_channels))
	    || alsa_error("hw_params_set_rate_near",
			  snd_pcm_hw_params_set_rate_near(alsa_hw.handle, hwp, &alsa_hw.rate, &dir))
	    || alsa_error("hw_params_set_access",
			  snd_pcm_hw_params_set_access(alsa_hw.handle, hwp, SND_PCM_ACCESS_RW_INTERLEAVED))
	    || alsa_error("hw_params_set_buffer_time_near",
			  snd_pcm_hw_params_set_buffer_time_near(alsa_hw.handle, hwp, &alsa_hw.buffer_time, 0)))
		return -1;

	/* How often to call our SIGIO handler (~40Hz) */
	period_time = alsa_hw.buffer_time / 4;
	if (alsa_error
	    ("hw_params_set_period_time_near",
	     snd_pcm_hw_params_set_period_time_near(alsa_hw.handle, hwp, &period_time, &dir))
	    || alsa_error("hw_params_get_buffer_size", snd_pcm_hw_params_get_buffer_size(hwp, &buffer_size))
	    || alsa_error("hw_params_get_period_size", snd_pcm_hw_params_get_period_size(hwp, &period_size, 0))
	    || alsa_error("hw_params", snd_pcm_hw_params(alsa_hw.handle, hwp)))
		return -1;

	snd_pcm_sw_params_current(alsa_hw.handle, swp);
	if (alsa_error
	    ("sw_params_set_start_threshold", snd_pcm_sw_params_set_start_threshold(alsa_hw.handle, swp, period_size))
	    || alsa_error("sw_params_set_avail_min", snd_pcm_sw_params_set_avail_min(alsa_hw.handle, swp, period_size))
	    || alsa_error("sw_params", snd_pcm_sw_params(alsa_hw.handle, swp)))
		return -1;

	return 0;
}
Пример #14
0
static gboolean
alsa_open (void *dp)
{
    alsa_driver * const d = dp;
    gint mf, err, pers;

    if(pcm_open_and_load_hwparams(d) < 0)
	goto out;

    // ---
    // Set non-blocking mode.
    // ---

    d->outtime = 0;

    // --
    // Set channel parameters
    // --
    if((err = snd_pcm_hw_params_set_access(d->soundfd, d->hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
	alsa_error(N_("Unable to set access"), err);
	goto out;
    }

    if((err = snd_pcm_hw_params_set_format(d->soundfd, d->hwparams,
				    (d->bits - 8) ? d->signedness16 ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U16 :
						    d->signedness8 ? SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_U8) < 0)) {
	alsa_error(N_("Unable to set audio format"), err);
	goto out;
    }
    /* Handle endianess aspects. TODO! */
    switch(d->bits) {
	case 16:
	    mf = d->signedness16 ? ST_MIXER_FORMAT_S16_LE : ST_MIXER_FORMAT_U16_LE;
	    break;
	case 8:
	default:
	    mf = d->signedness8 ? ST_MIXER_FORMAT_S8 : ST_MIXER_FORMAT_U8;
	    break;
    }

    if((err = snd_pcm_hw_params_set_channels(d->soundfd, d->hwparams, d->stereo + 1)) < 0) {
	alsa_error(N_("Unable to set channels number"), err);
	goto out;
    }
    if((d->stereo)) {
	mf |= ST_MIXER_FORMAT_STEREO;
    }
    d->mf = mf;

    d->p_mixfreq = d->playrate;
    if ((err = snd_pcm_hw_params_set_rate_near(d->soundfd, d->hwparams, &(d->p_mixfreq), NULL)) < 0) {
	alsa_error(N_("Unable to set sample rate"), err);
	goto out;
    }

    if(snd_pcm_hw_params_set_buffer_size(d->soundfd, d->hwparams, 1 << d->buffer_size) < 0) {
	/* Some soundcards report wrong maximal buffer size (maybe alsa bug). So we should try
	   to downscale its value before the reporting an error. The spinbutton still may display
	   the wrong number, but actually the correct value will be implemented.*/
	while((--d->buffer_size) >= 8)
	    if(!snd_pcm_hw_params_set_buffer_size(d->soundfd, d->hwparams, 1 << d->buffer_size))
		break;
	if(d->buffer_size < 8) {
	    error_error(N_("Unable to set appropriate buffer size"));
	    goto out;
	}
    }
    pers = 1 << d->num_periods;
    if ((err = snd_pcm_hw_params_set_periods_near(d->soundfd, d->hwparams, &pers, NULL)) < 0) {
	alsa_error(N_("Unable to set periods number"), err);
	goto out;
    }
    if ((err = snd_pcm_hw_params_get_period_size(d->hwparams, &(d->p_fragsize), NULL)) < 0) {
	alsa_error(N_("Unable to get period size"), err);
	goto out;
    }

    if((err = snd_pcm_hw_params(d->soundfd, d->hwparams))) {
	alsa_error(N_("Error setting hw parameters"), err);
	goto out;
    }

    /* The following piece of code is directly c_n_p'ed from the ALSA pcm example (whith a little adopting) */
    /* get the current swparams */
    err = snd_pcm_sw_params_current(d->soundfd, d->swparams);
    if (err < 0) {
            alsa_error(N_("Unable to determine current swparams for playback"), err);
	    goto out;
    }
    /* start the transfer when the buffer is full */
    err = snd_pcm_sw_params_set_start_threshold(d->soundfd, d->swparams, d->p_fragsize);
    if (err < 0) {
            alsa_error(N_("Unable to set start threshold mode for playback"), err);
	    goto out;
    }
    /* allow the transfer when at least period_size samples can be processed */
    err = snd_pcm_sw_params_set_avail_min(d->soundfd, d->swparams, d->p_fragsize);
    if (err < 0) {
            alsa_error(N_("Unable to set avail min for playback"), err);
	    goto out;
    }
    /* align all transfers to 1 sample */
    err = snd_pcm_sw_params_set_xfer_align(d->soundfd, d->swparams, 1);
    if (err < 0) {
            alsa_error(N_("Unable to set transfer align for playback"), err);
	    goto out;
    }
    /* write the parameters to the playback device */
    err = snd_pcm_sw_params(d->soundfd, d->swparams);
    if (err < 0) {
            alsa_error(N_("Unable to set sw params for playback"), err);
	    goto out;
    }

    err = snd_pcm_prepare(d->soundfd);
    if (err < 0) {
            alsa_error(N_("Unable to prepare playback"), err);
	    goto out;
    }
    // ---
    // Get buffering parameters
    // ---
   
    if(d->verbose)
        snd_pcm_dump(d->soundfd, d->output);
    d->sndbuf = calloc((d->stereo + 1) * (d->bits / 8), d->p_fragsize);

    d->pfd = malloc(sizeof(struct pollfd));
    if ((err = snd_pcm_poll_descriptors(d->soundfd, d->pfd, 1)) < 0) {
        alsa_error(N_("Unable to obtain poll descriptors for playback"), err);
        goto out;
    }

    d->polltag = audio_poll_add(d->pfd->fd, GDK_INPUT_WRITE, alsa_poll_ready_playing, d);

    d->playtime = 0;
    d->firstpoll = TRUE;

    return TRUE;

  out:
    alsa_release(dp);
    return FALSE;
}
Пример #15
0
int audio_alsa_init()
{
	int fd, err;
	char *pcm_rate;
	char tmp_name[20];

	init_rec_buffer();

	/* Create a temporary filename for our FIFO,
	 * Use mkstemp() instead of mktemp() although we need a FIFO not a
	 * regular file. We do this since glibc barfs at mktemp() and this
	 * scares the users :-)
	 */
	strcpy(tmp_name, "/tmp/lircXXXXXX");
	fd = mkstemp(tmp_name);
	close(fd);

	/* Start the race! */
	unlink(tmp_name);
	if (mknod(tmp_name, S_IFIFO | S_IRUSR | S_IWUSR, 0)) {
		logprintf(LOG_ERR, "could not create FIFO %s", tmp_name);
		logperror(LOG_ERR, "audio_alsa_init ()");
		return 0;
	}
	/* Phew, we won the race ... */

	/* Open the pipe and hand it to LIRC ... */
	hw.fd = open(tmp_name, O_RDWR);
	if (hw.fd < 0) {
		logprintf(LOG_ERR, "could not open pipe %s", tmp_name);
		logperror(LOG_ERR, "audio_alsa_init ()");
error:		unlink(tmp_name);
		audio_alsa_deinit();
		return 0;
	}

	/* Open the other end of the pipe and hand it to ALSA code.
	 * We're opening it in non-blocking mode to avoid lockups.
	 */
	alsa_hw.fd = open(tmp_name, O_RDWR | O_NONBLOCK);
	/* Ok, we don't need the FIFO visible in the filesystem anymore ... */
	unlink(tmp_name);

	/* Examine the device name, if it contains a sample rate */
	strncpy(tmp_name, hw.device, sizeof(tmp_name) - 1);
	pcm_rate = strchr(tmp_name, '@');
	if (pcm_rate) {
		int rate;
		char *stereo_channel;

		/* Examine if we need to capture in stereo
		 * looking for an 'l' or 'r' character to indicate
		 * which channel to look at.*/
		stereo_channel = strchr(pcm_rate, ',');

		if (stereo_channel) {

			/* Syntax in device string indicates we need
			   to use stereo */
			alsa_hw.num_channels = 2;
			/* As we are requesting stereo now, use the
			   more common signed 16bit samples */
			alsa_hw.format = SND_PCM_FORMAT_S16_LE;

			if (stereo_channel[1] == 'l') {
				alsa_hw.channel = 0;
			} else if (stereo_channel[1] == 'r') {
				alsa_hw.channel = 1;
			} else {
				logperror(LOG_WARNING,
					  "dont understand which channel to use - defaulting to left\n");
			}
		}

		/* Remove the sample rate from device name (and
		   channel indicator if present) */
		*pcm_rate++ = 0;
		/* See if rate is meaningful */
		rate = atoi(pcm_rate);
		if (rate > 0) {
			alsa_hw.rate = rate;
		}
	}

	/* Open the audio card in non-blocking mode */
	err = snd_pcm_open(&alsa_hw.handle, tmp_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
	if (err < 0) {
		logprintf(LOG_ERR, "could not open audio device %s: %s", hw.device, snd_strerror(err));
		logperror(LOG_ERR, "audio_alsa_init ()");
		goto error;
	}

	/* Set up the I/O signal handler */
	if (alsa_error
	    ("async_add_handler", snd_async_add_pcm_handler(&alsa_hw.sighandler, alsa_hw.handle, alsa_sig_io, NULL)))
		goto error;

	/* Set sampling parameters */
	if (alsa_set_hwparams(alsa_hw.handle))
		goto error;

	LOGPRINTF(LOG_INFO, "hw_audio_alsa: Using device '%s', sampling rate %dHz\n", tmp_name, alsa_hw.rate);

	/* Start sampling data */
	if (alsa_error("start", snd_pcm_start(alsa_hw.handle)))
		goto error;

	return 1;
}
Пример #16
0
static void
device_test (GtkWidget *w, alsa_driver *d)
{
    guint chmin, chmax, i;
    gint err;
    gchar *new_device;

    d->can8 = FALSE;
    d->can16 = FALSE;
    d->canmono = FALSE;
    d->canstereo = FALSE;
    d->signedness8 = FALSE;
    d->signedness16 = FALSE;

    new_device = gtk_combo_box_get_active_text(GTK_COMBO_BOX(d->alsa_device));
    if(g_ascii_strcasecmp(d->device, new_device)) {
	g_free(d->device);
	d->device = g_strdup(new_device);
	gui_hlp_combo_box_prepend_text_or_set_active(GTK_COMBO_BOX(d->alsa_device), d->device, FALSE);
    }

    for(i = 0; i < NUM_FORMATS; i++){
	d->devcap[i].minfreq = 8000;
	d->devcap[i].maxfreq = 44100;
	d->devcap[i].minbufsize = 256;
    }
    d->devcap[MONO8].maxbufsize = 65536;
    d->devcap[STEREO8].maxbufsize = 32768;
    d->devcap[MONO16].maxbufsize = 32768;
    d->devcap[STEREO16].maxbufsize = 16384;

    if(pcm_open_and_load_hwparams(d) < 0)
	return;

    if(!snd_pcm_hw_params_test_format(d->soundfd, d->hwparams, SND_PCM_FORMAT_U8)) {
	d->can8 = TRUE;
    }
    if(!snd_pcm_hw_params_test_format(d->soundfd, d->hwparams, SND_PCM_FORMAT_S8)) {
	d->can8 = TRUE;
	d->signedness8 = TRUE;
    }
    if(!snd_pcm_hw_params_test_format(d->soundfd, d->hwparams, SND_PCM_FORMAT_U16)) {
	d->can16 = TRUE;
    }
    if(!snd_pcm_hw_params_test_format(d->soundfd, d->hwparams, SND_PCM_FORMAT_S16)) {
	d->can16 = TRUE;
	d->signedness16 = TRUE;
    }

    if((err = snd_pcm_hw_params_get_channels_min(d->hwparams, &chmin)) < 0) {
	alsa_error(N_("Unable to get minimal channels number"), err);
	snd_pcm_close(d->soundfd);
	return;
    }
    if((err = snd_pcm_hw_params_get_channels_max(d->hwparams, &chmax)) < 0) {
	alsa_error(N_("Unable to get maximal channels number"), err);
	snd_pcm_close(d->soundfd);
	return;
    }
    if(chmin > 2) {
	error_error("Both mono and stereo are not supported by ALSA device!!!");
	snd_pcm_close(d->soundfd);
	return;
    }
    if(chmin == 1)
	d->canmono = TRUE;
    if(chmax >= 2)
	d->canstereo = TRUE;

    if(d->can8) {
	if((err = snd_pcm_hw_params_set_format(d->soundfd, d->hwparams,
					d->signedness8 ? SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_U8)) < 0) {
	    alsa_error(N_("Unable to set audio format"), err);
	    snd_pcm_close(d->soundfd);
	    return;
	}
	if(d->canmono) {
	    if(set_rates(d, 1, MONO8) < 0) {
		snd_pcm_close(d->soundfd);
		return;
	    }
	}

	if(d->canstereo) {
	    snd_pcm_close(d->soundfd);
	    if(pcm_open_and_load_hwparams(d) < 0)
		return;
	    if(set_rates(d, 2, STEREO8) < 0) {
		snd_pcm_close(d->soundfd);
		return;
	    }
	}
    }

    if(d->can16) {
	snd_pcm_close(d->soundfd);
	if(pcm_open_and_load_hwparams(d) < 0)
	    return;
	if((err = snd_pcm_hw_params_set_format(d->soundfd, d->hwparams,
					d->signedness16 ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U16)) < 0) {
	    alsa_error(N_("Unable to set audio format"), err);
	    snd_pcm_close(d->soundfd);
	    return;
	}
	if(d->canmono) {
	    if(set_rates(d, 1, MONO16) < 0) {
		snd_pcm_close(d->soundfd);
		return;
	    }
	}
	if(d->canstereo) {
	    snd_pcm_close(d->soundfd);
	    if(pcm_open_and_load_hwparams(d) < 0)
		return;
	    if(set_rates(d, 2, STEREO16) < 0) {
		snd_pcm_close(d->soundfd);
		return;
	    }
	}
    }

    snd_pcm_close(d->soundfd);
    update_controls(d);
}
Пример #17
0
static void
check_period_sizes (alsa_driver *d)
{
    /* Almost everything (luckily except sampling frequency) can affect period size: buffer size,
       format, channel numbers... So we need to recheck it after at least one of the parameters is
       changed -- mutab0r */
    gint err;
    guint address = PARAMS_TO_ADDRESS(d);

    /* The procedure is time-consuming and may cause audio system lock. So be sure if we really
       need it before starting... -- mutab0r */
    if(d->bits == 0)
	return;
    if((address == d->address_old) && (d->buffer_size == d->bufsize_old))
	return;

    if(pcm_open_and_load_hwparams(d) < 0)
	return;

    if((err = snd_pcm_hw_params_set_format(d->soundfd, d->hwparams,
				    (d->bits - 8) ? d->signedness16 ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U16 :
						    d->signedness8 ? SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_U8)) < 0) {
	alsa_error(N_("Unable to set audio format"), err);
	snd_pcm_close(d->soundfd);
	return;
    }
    if((err = snd_pcm_hw_params_set_channels(d->soundfd, d->hwparams, d->stereo + 1)) < 0) {
	alsa_error(N_("Unable to set channels number"), err);
	snd_pcm_close(d->soundfd);
	return;
    }
    if(snd_pcm_hw_params_set_buffer_size(d->soundfd, d->hwparams, 1 << d->buffer_size) < 0) {
	/* Some soundcards report wrong maximal buffer size (maybe alsa bug). So we should try
	   to downscale its value before the reporting an error. The spinbutton still may display
	   the wrong number, but actually the correct value will be implemented.*/
	while((--d->buffer_size) >= 8)
	    if(!snd_pcm_hw_params_set_buffer_size(d->soundfd, d->hwparams, 1 << d->buffer_size))
		break;
	if(d->buffer_size < 8) {
	    error_error(N_("Unable to set appropriate buffer size"));
	    snd_pcm_close(d->soundfd);
	    return;
	}
    }

    snd_pcm_close(d->soundfd);

    if ((err = snd_pcm_hw_params_get_period_size_min(d->hwparams, &(d->persizemin), 0)) < 0) {
	alsa_error(N_("Unable to get minimal period size"), err);
	return;
    }
    if ((err = snd_pcm_hw_params_get_period_size_max(d->hwparams, &(d->persizemax), 0)) < 0) {
	alsa_error(N_("Unable to get maximal period size"), err);
	return;
    }

    update_periods_range(d);
    update_estimate(d);

    d->address_old = address;
    d->bufsize_old = d->buffer_size;
}
Пример #18
0
static void alsa_sig_io(snd_async_handler_t * h)
{
	/* Previous sample */
	static unsigned char ps = 0x80;
	/* Count samples with similar level (to detect pule/space
	   length), 24.8 fp */
	static unsigned sample_count = 0;
	/* Current signal level (dynamically changes) */
	static unsigned signal_level = 0;
	/* Current state (pulse or space) */
	static unsigned signal_state = 0;
	/* Signal maximum and minimum (used for "zero" detection) */
	static unsigned char signal_max = 0x80, signal_min = 0x80;
	/* Non-zero if we're in zero crossing waiting state */
	static char waiting_zerox = 0;
	/* Store sample size, as our sample buffer will represent
	   shorts or chars */
	unsigned char bytes_per_sample = (alsa_hw.format == SND_PCM_FORMAT_S16_LE ? 2 : 1);

	int i, err;
	char buff[READ_BUFFER_SIZE];
	snd_pcm_sframes_t count;

	/* The value to multiply with number of samples to get microseconds
	 * (fixed-point 24.8 bits).
	 */
	unsigned mulconst = 256000000 / alsa_hw.rate;
	/* Maximal number of samples that can be multiplied by mulconst */
	unsigned maxcount = (((PULSE_MASK << 8) | 0xff) / mulconst) << 8;

	/* First of all, check for underrun. This happens, for example, when
	 * the X11 server starts. If we won't, recording will stop forever.
	 */
	snd_pcm_state_t state = snd_pcm_state(alsa_hw.handle);
	switch (state) {
	case SND_PCM_STATE_SUSPENDED:
		while ((err = snd_pcm_resume(alsa_hw.handle)) == -EAGAIN)
			/* wait until the suspend flag is released */
			sleep(1);
		if (err >= 0)
			goto var_reset;
		/* Fallthrough */
	case SND_PCM_STATE_XRUN:
		alsa_error("prepare", snd_pcm_prepare(alsa_hw.handle));
		alsa_error("start", snd_pcm_start(alsa_hw.handle));
var_reset:			/* Reset variables */
		sample_count = 0;
		waiting_zerox = 0;
		signal_level = 0;
		signal_state = 0;
		signal_max = signal_min = 0x80;
		break;
	default:
		/* Stream is okay */
		break;
	}

	/* Read all available data */
	if ((count = snd_pcm_avail_update(alsa_hw.handle)) > 0) {
		if (count > (READ_BUFFER_SIZE / (bytes_per_sample * alsa_hw.num_channels)))
			count = READ_BUFFER_SIZE / (bytes_per_sample * alsa_hw.num_channels);
		count = snd_pcm_readi(alsa_hw.handle, buff, count);

		/*Loop around samples, if stereo we are
		 *only interested in one channel*/
		for (i = 0; i < count; i++) {
			/* cs == current sample */
			unsigned char cs, as, sl, sz, xz;

			if (bytes_per_sample == 2) {
				cs = ((*(short *)
				       &buff[i * bytes_per_sample * alsa_hw.num_channels +
					     bytes_per_sample * alsa_hw.channel]) >> 8);
				cs ^= 0x80;
			} else {
				cs = buff[i];

				/* Convert signed samples to unsigned */
				if (alsa_hw.format == SND_PCM_FORMAT_S8) {
					cs ^= 0x80;
				}
			}

			/* Track signal middle value (it could differ from 0x80) */
			sz = (signal_min + signal_max) / 2;
			if (cs <= sz)
				signal_min = (signal_min * 7 + cs) / 8;
			if (cs >= sz)
				signal_max = (signal_max * 7 + cs) / 8;

			/* Compute the absolute signal deviation from middle */
			as = U8_ABSDIFF(cs, sz);

			/* Integrate incoming signal (auto level adjustment) */
			signal_level = (signal_level * 7 + as) / 8;

			/* Don't let too low signal levels as it makes us sensible to noise */
			sl = signal_level;
			if (sl < 16)
				sl = 16;

			/* Detect crossing current "zero" level */
			xz = ((cs - sz) ^ (ps - sz)) & 0x80;

			/* Don't wait for zero crossing for too long */
			if (waiting_zerox && !xz)
				waiting_zerox--;

			/* Detect significant signal level changes */
			if ((abs(cs - ps) > sl / 2) && xz)
				waiting_zerox = 2;

			/* If we have crossed zero with a substantial level change, go */
			if (waiting_zerox && xz) {
				lirc_t x;

				waiting_zerox = 0;

				if (sample_count >= maxcount) {
					x = PULSE_MASK;
					sample_count = 0;
				} else {
					/**
					 * Try to interpolate the samples and determine where exactly
					 * the zero crossing point was. This is required as the
					 * remote signal frequency is relatively close to our sampling
					 * frequency thus a sampling error of 1 sample can lead to
					 * substantial time differences.
					 *
					 *     slope = (x2 - x1) / (y2 - y1)
					 *     x = x1 + (y - y1) * slope
					 *
					 * where x1=-1, x2=0, y1=ps, y2=cs, y=sz, thus:
					 *
					 *     x = -1 + (y - y1) / (y2 - y1), or
					 * ==> x = (y - y2) / (y2 - y1)
					 *
					 * y2 (cs) cannot be equal to y1 (ps), otherwise we wouldn't
					 * get here.
					 */
					int delta = (((int)sz - (int)cs) << 8) / ((int)cs - (int)ps);
					/* This expression can easily overflow the 'long' value since it
					 * multiplies two 24.8 values (and we get a 24.16 instead).
					 * To avoid this we cast the intermediate value to "long long".
					 */
					x = (((long long)sample_count + delta) * mulconst) >> 16;
					/* The rest of the quantum is on behalf of next pulse. Note that
					 * sample_count can easily be assigned here a negative value (in
					 * the case zero crossing occurs during the next quantum).
					 */
					sample_count = -delta;
				}

				/* Consider impossible pulses with length greater than
				 * 0.02 seconds, thus it is a space (desynchronization).
				 */
				if ((x > 20000) && signal_state) {
					signal_state = 0;
					LOGPRINTF(1, "Pulse/space desynchronization fixed - len %u", x);
				}

				x |= signal_state;

				/* Write the LIRC code to the FIFO */
				write(alsa_hw.fd, &x, sizeof(x));

				signal_state ^= PULSE_BIT;
			}
Пример #19
0
static int pcm_open(struct alsa_pcm *alsa, const char *device_name,
                    snd_pcm_stream_t stream, int rate, int buffer_time)
{
    int r, dir;
    unsigned int p;
    size_t bytes;
    snd_pcm_hw_params_t *hw_params;
    
    r = snd_pcm_open(&alsa->pcm, device_name, stream, SND_PCM_NONBLOCK);
    if (r < 0) {
        alsa_error("open", r);
        return -1;
    }

    snd_pcm_hw_params_alloca(&hw_params);

    r = snd_pcm_hw_params_any(alsa->pcm, hw_params);
    if (r < 0) {
        alsa_error("hw_params_any", r);
        return -1;
    }
    
    r = snd_pcm_hw_params_set_access(alsa->pcm, hw_params,
                                     SND_PCM_ACCESS_RW_INTERLEAVED);
    if (r < 0) {
        alsa_error("hw_params_set_access", r);
        return -1;
    }
    
    r = snd_pcm_hw_params_set_format(alsa->pcm, hw_params, SND_PCM_FORMAT_S16);
    if (r < 0) {
        alsa_error("hw_params_set_format", r);
        fprintf(stderr, "16-bit signed format is not available. "
                "You may need to use a 'plughw' device.\n");
        return -1;
    }

    r = snd_pcm_hw_params_set_rate(alsa->pcm, hw_params, rate, 0);
    if (r < 0) {
        alsa_error("hw_params_set_rate", r);
        fprintf(stderr, "%dHz sample rate not available. You may need to use "
                "a 'plughw' device.\n", rate);
        return -1;
    }
    alsa->rate = rate;

    r = snd_pcm_hw_params_set_channels(alsa->pcm, hw_params, DEVICE_CHANNELS);
    if (r < 0) {
        alsa_error("hw_params_set_channels", r);
        fprintf(stderr, "%d channel audio not available on this device.\n",
                DEVICE_CHANNELS);
        return -1;
    }

    p = buffer_time * 1000; /* microseconds */
    dir = -1;
    r = snd_pcm_hw_params_set_buffer_time_max(alsa->pcm, hw_params, &p, &dir);
    if (r < 0) {
        alsa_error("hw_params_set_buffer_time_max", r);
        fprintf(stderr, "Buffer of %dms may be too small for this hardware.\n",
                buffer_time);
        return -1;
    }

    p = 2; /* double buffering */
    dir = 1;
    r = snd_pcm_hw_params_set_periods_min(alsa->pcm, hw_params, &p, &dir);
    if (r < 0) {
        alsa_error("hw_params_set_periods_min", r);
        fprintf(stderr, "Buffer of %dms may be too small for this hardware.\n",
                buffer_time);
        return -1;
    }

    r = snd_pcm_hw_params(alsa->pcm, hw_params);
    if (r < 0) {
        alsa_error("hw_params", r);
        return -1;
    }
    
    r = snd_pcm_hw_params_get_period_size(hw_params, &alsa->period, &dir);
    if (r < 0) {
        alsa_error("get_period_size", r);
        return -1;
    }

    bytes = alsa->period * DEVICE_CHANNELS * sizeof(signed short);
    alsa->buf = malloc(bytes);
    if (!alsa->buf) {
        perror("malloc");
        return -1;
    }

    /* snd_pcm_readi() returns uninitialised memory on first call,
     * possibly caused by premature POLLIN. Keep valgrind happy. */

    memset(alsa->buf, 0, bytes);

    return 0;
}
Пример #20
0
static int handle(struct device *dv)
{
    int r;
    unsigned short revents;
    struct alsa *alsa = (struct alsa*)dv->local;

    /* Check input buffer for timecode capture */
    
    r = pcm_revents(&alsa->capture, &revents);
    if (r < 0)
        return -1;
    
    if (revents & POLLIN) {
        r = capture(dv);
        
        if (r < 0) {
            if (r == -EPIPE) {
                fputs("ALSA: capture xrun.\n", stderr);

                r = snd_pcm_prepare(alsa->capture.pcm);
                if (r < 0) {
                    alsa_error("prepare", r);
                    return -1;
                }

                r = snd_pcm_start(alsa->capture.pcm);
                if (r < 0) {
                    alsa_error("start", r);
                    return -1;
                }

            } else {
                alsa_error("capture", r);
                return -1;
            }
        } 
    }
    
    /* Check the output buffer for playback */
    
    r = pcm_revents(&alsa->playback, &revents);
    if (r < 0)
        return -1;
    
    if (revents & POLLOUT) {
        r = playback(dv);
        
        if (r < 0) {
            if (r == -EPIPE) {
                fputs("ALSA: playback xrun.\n", stderr);
                
                r = snd_pcm_prepare(alsa->playback.pcm);
                if (r < 0) {
                    alsa_error("prepare", r);
                    return -1;
                }

                /* The device starts when data is written. POLLOUT
                 * events are generated in prepared state. */

            } else {
                alsa_error("playback", r);
                return -1;
            }
        }
    }

    return 0;
}