IC-7410 Audio Interface and ALSA (3)

The original pcm.c has 925 lines to send a sine wave with various transfer methods. Here is a simplified version only to support asynchronous notification, and instead of playing a waveform, the audio signal is captured from the device.

capture

The source program is compiled, the audio signal from IC-7410 (hw:2,0) is captured and stored in a file, and the file is played back on a speaker (hw:1,3).

/* file name = pcm_capture3.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include "asoundlib.h"
#include <sys/time.h>
#include <math.h>

static char *device             = "hw:2,0";	/* playback device is IC-7410*/
static unsigned int rate        = 44100;	/* stream rate */
static unsigned int channels    = 2;		/* count of channels */
static int byte_per_sample      = 2;		/* 16 bit format */
static unsigned int buffer_time = 500000;	/* ring buffer length in us */
static unsigned int period_time = 100000;	/* period time in us */
static int resample             = 0;		/* enable alsa-lib resampling */
static int period_event         = 0;		/* produce poll event after each period */

static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;

static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params)
{
	unsigned int      rrate;
	snd_pcm_uframes_t size;
	int               err, dir;

	/* choose all parameters */
	err = snd_pcm_hw_params_any(handle, params);
	if (err < 0) {
		fprintf(stderr,"Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
		return err;
	}

	/* set hardware resampling */
	err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
	if (err < 0) {
		fprintf(stderr,"Resampling setup failed for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the interleaved read/write format */
	err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
	if (err < 0) {
		fprintf(stderr,"Access type not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the sample format */
	err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16);
	if (err < 0) {
		fprintf(stderr,"Sample format not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the count of channels */
	err = snd_pcm_hw_params_set_channels(handle, params, channels);
	if (err < 0) {
		fprintf(stderr,"Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
		return err;
	}

	/* set the stream rate */
	rrate = rate;
	err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
	if (err < 0) {
		fprintf(stderr,"Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
		return err;
	}
	if (rrate != rate) {
		fprintf(stderr,"Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
		return -EINVAL;
	}

	/* set the buffer time */
	err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
	if (err < 0) {
		fprintf(stderr,"Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_buffer_size(params, &size);
	if (err < 0) {
		fprintf(stderr,"Unable to get buffer size for playback: %s\n", snd_strerror(err));
		return err;
	}
	buffer_size = size;

	/* set the period time */
	err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
	if (err < 0) {
		fprintf(stderr,"Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
	if (err < 0) {
		fprintf(stderr,"Unable to get period size for playback: %s\n", snd_strerror(err));
		return err;
	}
	period_size = size;

	/* write the parameters to device */
	err = snd_pcm_hw_params(handle, params);
	if (err < 0) {
		fprintf(stderr,"Unable to set hw params for playback: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
{
	int err;

	/* get the current swparams */
	err = snd_pcm_sw_params_current(handle, swparams);
	if (err < 0) {
		fprintf(stderr,"Unable to determine current swparams for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* start the transfer when the buffer is almost full: */
	/* (buffer_size / avail_min) * avail_min */
	err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
	if (err < 0) {
		fprintf(stderr,"Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* allow the transfer when at least period_size samples can be processed */
	/* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
	err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
	if (err < 0) {
		fprintf(stderr,"Unable to set avail min for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* enable period events when requested */
	if (period_event) {
		err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
		if (err < 0) {
			fprintf(stderr,"Unable to set period event: %s\n", snd_strerror(err));
			return err;
		}
	}

	/* write the parameters to the playback device */
	err = snd_pcm_sw_params(handle, swparams);
	if (err < 0) {
		fprintf(stderr,"Unable to set sw params for playback: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

static void async_callback(snd_async_handler_t *ahandler)
{
	snd_pcm_t                 *handle  = snd_async_handler_get_pcm(ahandler);
	signed short              *samples = snd_async_handler_get_callback_private(ahandler);
	snd_pcm_sframes_t          avail;
	int                        err, write_count, wc;
	
	avail = snd_pcm_avail_update(handle);
	while (avail >= period_size) {
		err = snd_pcm_readi (handle, samples, period_size);
		if (err < 0) {
			fprintf(stderr,"Write error: %s\n", snd_strerror(err));
			exit(EXIT_FAILURE);
		}
		if (err != period_size) {
			fprintf(stderr,"Write error: written %i expected %li\n", err, period_size);
			exit(EXIT_FAILURE);
		}

		write_count = period_size * channels * byte_per_sample;
		wc = write(1, samples, write_count);
		if(wc != write_count) {
			fprintf(stderr,"file write error. \n");
		}

		avail = snd_pcm_avail_update(handle);
	}
}

static int async_loop(snd_pcm_t *handle, signed short *samples)
{
	snd_async_handler_t       *ahandler;
	int                        err;

	err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, samples);
	if (err < 0) {
		fprintf(stderr,"Unable to register async handler\n");
		exit(EXIT_FAILURE);
	}

	if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) {
		err = snd_pcm_start(handle);
		if (err < 0) {
			fprintf(stderr,"Start error: %s\n", snd_strerror(err));
			exit(EXIT_FAILURE);
		}
	}

	while (1) {
		sleep(1);
	}
}

int main(int argc, char *argv[])
{
	snd_pcm_t              *handle;
	snd_pcm_hw_params_t    *hwparams;
	snd_pcm_sw_params_t    *swparams;
	signed short           *samples;
	int                     err;

	snd_pcm_hw_params_alloca(&hwparams);
	snd_pcm_sw_params_alloca(&swparams);

	fprintf(stderr,"capture device = %s, rate = %i Hz, %i channels\n", device, rate, channels);

	if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
		fprintf(stderr,"Capture open error: %s\n", snd_strerror(err));
		return 0;
	}
	
	if ((err = set_hwparams(handle, hwparams)) < 0) {
		fprintf(stderr,"Setting of hwparams failed: %s\n", snd_strerror(err));
		exit(EXIT_FAILURE);
	}
	if ((err = set_swparams(handle, swparams)) < 0) {
		fprintf(stderr,"Setting of swparams failed: %s\n", snd_strerror(err));
		exit(EXIT_FAILURE);
	}

	samples = malloc(period_size * channels * byte_per_sample);
	if (samples == NULL) {
		fprintf(stderr,"No enough memory\n");
		exit(EXIT_FAILURE);
	}
	
	err = async_loop(handle, samples);
	if (err < 0) fprintf(stderr,"Transfer failed: %s\n", snd_strerror(err));

	free(samples);
	snd_pcm_close(handle);
	return 0;
}

Recording Computer Playback

Suppose you are listening to something on your PC, and you wish to record that sound. Here is a solution.

volume1

The sound is being played back on one of the output devices of your choice, an HDMI audio output in this case.

volume2

You can use, say, Audacity, for recording.

volume3

Use PulseAudio Volume Control (pavucontrol) to select the device you wish to capture from, which is the device you are playing the sound with.

http://manual.audacityteam.org/o/man/tutorial_recording_computer_playback_on_linux.html