All of lore.kernel.org
 help / color / mirror / Atom feed
* io-plugin does not call start callback function
@ 2009-12-03 11:49 Stefan Schoenleitner
  2009-12-03 15:51 ` Stefan Schoenleitner
  0 siblings, 1 reply; 8+ messages in thread
From: Stefan Schoenleitner @ 2009-12-03 11:49 UTC (permalink / raw)
  To: alsa-devel@alsa-project.org

Hi,

I'm writing an io plugin that should transfer all PCM samples to a UNIX socket.

For this reason in my plugin I open the socket and then fill the plugin struct like this:

socketdump->io.mmap_rw = 0;
socketdump->io.poll_fd = socketdump->sock_fd;
socketdump->io.poll_events = POLLOUT;

Thus for my understanding (the alsa doc is really bad), every time
the socket file descriptor is available for writing the alsa plugin would generally speaking issue a data transfer.

However, when playing audio over that plugin for some reason the start callback function is never called.
Instead I get output like this:

socketdump_hw_constraint:417 26944 
_snd_pcm_socketdump_open:585 26944 SND_PCM_PLUGIN_DEFINE_FUNC(): done, err=0

Playing WAVE 'test_8khz_16LE_mono.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Mono
snd_pcm_socketdump_prepare:309 26944 Preparing with io->period_size=164 io->buffer_size=3936
snd_pcm_socketdump_pointer:229 26944 hw_ptr: 0 ()
snd_pcm_socketdump_write:268 26944 snd_pcm_socketdump_write(): areas->step=16 areas->first=0 areas->addr=0x22f1f60 offset=0, size=164 io->nonblock=0
......


Do you have any idea why the start callback function is not called at all ?

cheers,
stefan

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: io-plugin does not call start callback function
  2009-12-03 11:49 io-plugin does not call start callback function Stefan Schoenleitner
@ 2009-12-03 15:51 ` Stefan Schoenleitner
       [not found]   ` <358341420912040013m526a834bl968668da7b9f3c07@mail.gmail.com>
  0 siblings, 1 reply; 8+ messages in thread
From: Stefan Schoenleitner @ 2009-12-03 15:51 UTC (permalink / raw)
  To: alsa-devel@alsa-project.org

I wrote some minimalistic io plugin code that shows that start() is *never* called.
Is this a bug in alsa ?

output:

aplay -D play test_8khz_16LE_mono.wav
Playing WAVE 'test_8khz_16LE_mono.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Mono
snd_pcm_play_hw_params:71 24118 
snd_pcm_play_prepare:64 24118 
snd_pcm_play_pointer:49 24118 
snd_pcm_play_transfer:58 24118 aborting with -EINVAL ...
aplay: pcm_write:1442: write error: Invalid argument


plugin code:

#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <time.h>
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>

#define FILE_PERM	0644
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

#define DEBUG
#ifdef DEBUG
#define DBG(f, ...) \
    fprintf(stderr, "%s:%i %i "f"\n", __FUNCTION__, __LINE__, getpid(), ## __VA_ARGS__);
#else
#define DBG(f, ...)
#endif

struct play_info {
	snd_pcm_ioplug_t io;
	char *filename;
	int file_fd;
	snd_pcm_sframes_t hw_ptr;
	snd_pcm_hw_params_t *hw_params;
};

static int snd_pcm_play_close(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}
static int snd_pcm_play_start(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}
static int snd_pcm_play_stop(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}
static snd_pcm_sframes_t snd_pcm_play_pointer(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}

static snd_pcm_sframes_t snd_pcm_play_transfer(snd_pcm_ioplug_t *io,
			const snd_pcm_channel_area_t *areas,
			snd_pcm_uframes_t offset,
			snd_pcm_uframes_t size)
{
	DBG("aborting with -EINVAL ...");
	return -EINVAL;
}

static int snd_pcm_play_prepare(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}

static int snd_pcm_play_hw_params(snd_pcm_ioplug_t *io,
                    snd_pcm_hw_params_t *params)
{
	DBG("");
	return 0;
}

static int snd_pcm_play_poll_descriptors_count(snd_pcm_ioplug_t *io)
{
		DBG("");
	    return 1;
}


static int snd_pcm_play_poll_descriptors(snd_pcm_ioplug_t *io,
                    struct pollfd *pfd, unsigned int space)
{
	DBG("");
	return 1;
}

static int snd_pcm_play_poll_revents(snd_pcm_ioplug_t *io,
                    struct pollfd *pfds, unsigned int nfds,
	                unsigned short *revents)
{
	DBG("");
	return 0;
}

static int snd_pcm_play_delay(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp)
{
	DBG("");
	return 0;
}

static snd_pcm_ioplug_callback_t play_pcm_callback = {
    .close = snd_pcm_play_close,
    .start = snd_pcm_play_start,
    .stop = snd_pcm_play_stop,
    .pointer = snd_pcm_play_pointer,
    .transfer = snd_pcm_play_transfer,
	.prepare = snd_pcm_play_prepare,
	.hw_params = snd_pcm_play_hw_params,
	.poll_descriptors_count = snd_pcm_play_poll_descriptors_count,
	.poll_descriptors = snd_pcm_play_poll_descriptors,
	.poll_revents = snd_pcm_play_poll_revents,
	.delay = snd_pcm_play_delay,
};

static int play_hw_constraint(struct play_info * pcm)
{
	snd_pcm_ioplug_t *io = &pcm->io;


	static const snd_pcm_access_t access_list[] = {
		SND_PCM_ACCESS_RW_INTERLEAVED
	};
	static const unsigned int formats[] = {
		SND_PCM_FORMAT_S16_LE,
	};

	int err;

	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
					    ARRAY_SIZE(access_list),
					    access_list);
	if (err < 0)
	{
		SNDERR("cannot set HW access constraint list: %s", snd_strerror(err));
		return err;
	}

	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
					    ARRAY_SIZE(formats), formats);
	if (err < 0)
	{
		SNDERR("cannot set HW format constraint list: %s", snd_strerror(err));
		return err;
	}

	return 0;
}

SND_PCM_PLUGIN_DEFINE_FUNC(play)
{
	snd_config_iterator_t i, next;
	const char *filename=NULL;
	int err;
	struct play_info *play=NULL;

	if (stream != SND_PCM_STREAM_PLAYBACK)
	{
		SNDERR("play plugin can only be used for playback\n");
		return -1;
	}
	
	snd_config_for_each(i, next, conf) {
		snd_config_t *n = snd_config_iterator_entry(i);
		const char *id;
		if (snd_config_get_id(n, &id) < 0)
			continue;

		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0)
			continue;

		if (strcmp(id, "filename") == 0) {
			if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING)
			{
				SNDERR("invalid type for %s\n", id);
				return -EINVAL;
			}

			if (snd_config_get_string(n, &filename)<0)
			{
				SNDERR("could not get filename for %s\n", id);
				return -EINVAL;
			}
			continue;
		}

		SNDERR("Unknown field %s", id);
		return -EINVAL;
	}

	if (!filename)
	{
		SNDERR("no filename defined or play plugin\n");
		return -EINVAL;
	}

	if ((play = calloc(1, sizeof(*play))) == NULL)
		return -ENOMEM;

	play->io.version = SND_PCM_IOPLUG_VERSION;
	play->io.name = "ALSA play Plugin";
	play->io.callback = &play_pcm_callback;
	play->io.private_data = play;
	play->io.mmap_rw = 0;

	if ((err = snd_pcm_ioplug_create(&play->io, name, stream, mode))<0)
	{
        free(play);
        return err;
    }
	
	err = play_hw_constraint(play);
	if (err < 0) {
		snd_pcm_ioplug_delete(&play->io);
		free(play);
		return err;
	}
	
	*pcmp = play->io.pcm;

	return err;
}

SND_PCM_PLUGIN_SYMBOL(play);

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: io-plugin does not call start callback function
       [not found]   ` <358341420912040013m526a834bl968668da7b9f3c07@mail.gmail.com>
@ 2009-12-06 15:12     ` Stefan Schoenleitner
  2009-12-06 15:20     ` Stefan Schoenleitner
  2009-12-06 22:02     ` Stefan Schoenleitner
  2 siblings, 0 replies; 8+ messages in thread
From: Stefan Schoenleitner @ 2009-12-06 15:12 UTC (permalink / raw)
  To: Raul Xiong; +Cc: alsa-devel@alsa-project.org

Hi Raul,

Raul Xiong wrote:
> Hi Stefan
> 
> This is because your pointer callback function is not implemented properly.
> "pointer" callback function should return the right position of the
> playback/capture buffer, apps over alsa framework will call
> snd_pcm_avail_update to get the position.
> Also, you should call snd_pcm_ioplug_set_param_minmax in your
> play_hw_constraint function to define the buffer size relevant parameters.


Well, I did what you suggested but the start() callback is still not called:

$ aplay -D play test_8khz_16LE_mono.wav
Playing WAVE 'test_8khz_16LE_mono.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Mono
snd_pcm_play_hw_params:84 24610 
snd_pcm_play_prepare:71 24610 hw_ptr -> 0
snd_pcm_play_pointer:54 24610 ptr: 0
snd_pcm_play_transfer:63 24610 aborting with -EINVAL ...
aplay: pcm_write:1442: write error: Invalid argument


Also, when transferring PCM samples from the ALSA buffer to somewhere else, it seems logical to me that the initial value of the hw_ptr has to be 0, right ?
As you can see above, the situation is still exactly the same and the start callback is never called at all.

The code for the plugin is below.

cheers,
stefan



#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <time.h>
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>

#define FILE_PERM	0644
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

#define DEBUG
#ifdef DEBUG
#define DBG(f, ...) \
    fprintf(stderr, "%s:%i %i "f"\n", __FUNCTION__, __LINE__, getpid(), ## __VA_ARGS__);
#else
#define DBG(f, ...)
#endif

struct play_info {
	snd_pcm_ioplug_t io;
	char *filename;
	int file_fd;
	snd_pcm_sframes_t hw_ptr;
	snd_pcm_hw_params_t *hw_params;
};

static int snd_pcm_play_close(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}

static int snd_pcm_play_start(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}

static int snd_pcm_play_stop(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}

static snd_pcm_sframes_t snd_pcm_play_pointer(snd_pcm_ioplug_t *io)
{
	struct play_info *data = io->private_data;
	
	DBG("ptr: %lu", data->hw_ptr);
	return data->hw_ptr;
}

static snd_pcm_sframes_t snd_pcm_play_transfer(snd_pcm_ioplug_t *io,
			const snd_pcm_channel_area_t *areas,
			snd_pcm_uframes_t offset,
			snd_pcm_uframes_t size)
{
	DBG("aborting with -EINVAL ...");
	return -EINVAL;
}

static int snd_pcm_play_prepare(snd_pcm_ioplug_t *io)
{
	struct play_info *data = io->private_data;
	
	DBG("hw_ptr -> 0");
	data->hw_ptr=0;

	return 0;
}

static int snd_pcm_play_hw_params(snd_pcm_ioplug_t *io,
                    snd_pcm_hw_params_t *params)
{
	snd_pcm_access_t access_list[] = { SND_PCM_ACCESS_RW_INTERLEAVED };
	unsigned int format_list[] = { SND_PCM_FORMAT_S16 };
	int err;
	
	DBG("");

    /* access type */
    err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
                    ARRAY_SIZE(access_list), access_list);
    if (err < 0)
        return err;

    /* supported formats */
    err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
                    ARRAY_SIZE(format_list), format_list);
    if (err < 0)
        return err;

    /* supported channels */
    err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
                            1, 1);
    if (err < 0)
        return err;

    /* supported rate */
    err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,
                            8000, 8000);
    if (err < 0)
        return err;

    /* supported block size */
    err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
                        156*2, 164*2);
    if (err < 0)
        return err;

    err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS,
                                    2, 200);
    if (err < 0)
        return err;

	return 0;
}

static int snd_pcm_play_poll_descriptors_count(snd_pcm_ioplug_t *io)
{
		DBG("");
	    return 1;
}


static int snd_pcm_play_poll_descriptors(snd_pcm_ioplug_t *io,
                    struct pollfd *pfd, unsigned int space)
{
	DBG("");
	return 1;
}

static int snd_pcm_play_poll_revents(snd_pcm_ioplug_t *io,
                    struct pollfd *pfds, unsigned int nfds,
	                unsigned short *revents)
{
	DBG("");
	return 0;
}

static int snd_pcm_play_delay(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp)
{
	DBG("");
	return 0;
}

static snd_pcm_ioplug_callback_t play_pcm_callback = {
    .close = snd_pcm_play_close,
    .start = snd_pcm_play_start,
    .stop = snd_pcm_play_stop,
    .pointer = snd_pcm_play_pointer,
    .transfer = snd_pcm_play_transfer,
	.prepare = snd_pcm_play_prepare,
	.hw_params = snd_pcm_play_hw_params,
	.poll_descriptors_count = snd_pcm_play_poll_descriptors_count,
	.poll_descriptors = snd_pcm_play_poll_descriptors,
	.poll_revents = snd_pcm_play_poll_revents,
	.delay = snd_pcm_play_delay,
};

static int play_hw_constraint(struct play_info * pcm)
{
	snd_pcm_ioplug_t *io = &pcm->io;


	static const snd_pcm_access_t access_list[] = {
		SND_PCM_ACCESS_RW_INTERLEAVED
	};
	static const unsigned int formats[] = {
		SND_PCM_FORMAT_S16_LE,
	};

	int err;

	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
					    ARRAY_SIZE(access_list),
					    access_list);
	if (err < 0)
	{
		SNDERR("cannot set HW access constraint list: %s", snd_strerror(err));
		return err;
	}

	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
					    ARRAY_SIZE(formats), formats);
	if (err < 0)
	{
		SNDERR("cannot set HW format constraint list: %s", snd_strerror(err));
		return err;
	}

	return 0;
}

SND_PCM_PLUGIN_DEFINE_FUNC(play)
{
	snd_config_iterator_t i, next;
	const char *filename=NULL;
	int err;
	struct play_info *play=NULL;

	if (stream != SND_PCM_STREAM_PLAYBACK)
	{
		SNDERR("play plugin can only be used for playback\n");
		return -1;
	}
	
	snd_config_for_each(i, next, conf) {
		snd_config_t *n = snd_config_iterator_entry(i);
		const char *id;
		if (snd_config_get_id(n, &id) < 0)
			continue;

		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0)
			continue;

		if (strcmp(id, "filename") == 0) {
			if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING)
			{
				SNDERR("invalid type for %s\n", id);
				return -EINVAL;
			}

			if (snd_config_get_string(n, &filename)<0)
			{
				SNDERR("could not get filename for %s\n", id);
				return -EINVAL;
			}
			continue;
		}

		SNDERR("Unknown field %s", id);
		return -EINVAL;
	}

	if (!filename)
	{
		SNDERR("no filename defined or play plugin\n");
		return -EINVAL;
	}

	if ((play = calloc(1, sizeof(*play))) == NULL)
		return -ENOMEM;

	play->io.version = SND_PCM_IOPLUG_VERSION;
	play->io.name = "ALSA play Plugin";
	play->io.callback = &play_pcm_callback;
	play->io.private_data = play;
	play->io.mmap_rw = 0;

	if ((err = snd_pcm_ioplug_create(&play->io, name, stream, mode))<0)
	{
        free(play);
        return err;
    }
	
	err = play_hw_constraint(play);
	if (err < 0) {
		snd_pcm_ioplug_delete(&play->io);
		free(play);
		return err;
	}
	
	*pcmp = play->io.pcm;

	return err;
}

SND_PCM_PLUGIN_SYMBOL(play);

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: io-plugin does not call start callback function
       [not found]   ` <358341420912040013m526a834bl968668da7b9f3c07@mail.gmail.com>
  2009-12-06 15:12     ` Stefan Schoenleitner
@ 2009-12-06 15:20     ` Stefan Schoenleitner
  2009-12-06 22:02     ` Stefan Schoenleitner
  2 siblings, 0 replies; 8+ messages in thread
From: Stefan Schoenleitner @ 2009-12-06 15:20 UTC (permalink / raw)
  To: Raul Xiong, alsa-devel@alsa-project.org

Hi Raul,

in my previous mail I put some snd_pcm_ioplug_set_param_minmax code in
the wrong callback function.
The corrected code should be below:


#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <time.h>
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>

#define FILE_PERM	0644
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

#define DEBUG

#ifdef DEBUG
#define DBG(f, ...) \
    fprintf(stderr, "%s:%i %i "f"\n", __FUNCTION__, __LINE__, getpid(),
## __VA_ARGS__);
#else
#define DBG(f, ...)
#endif

struct play_info {
	snd_pcm_ioplug_t io;
	char *filename;
	int file_fd;
	snd_pcm_sframes_t hw_ptr;
};

static int snd_pcm_play_close(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}

static int snd_pcm_play_start(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}

static int snd_pcm_play_stop(snd_pcm_ioplug_t *io)
{
	DBG("");
	return 0;
}

static snd_pcm_sframes_t snd_pcm_play_pointer(snd_pcm_ioplug_t *io)
{
	struct play_info *data = io->private_data;
	
	DBG("ptr: %lu", data->hw_ptr);
	return data->hw_ptr;
}

static snd_pcm_sframes_t snd_pcm_play_transfer(snd_pcm_ioplug_t *io,
			const snd_pcm_channel_area_t *areas,
			snd_pcm_uframes_t offset,
			snd_pcm_uframes_t size)
{
	DBG("aborting with -EINVAL ...");
	return -EINVAL;
}

static int snd_pcm_play_prepare(snd_pcm_ioplug_t *io)
{
	struct play_info *data = io->private_data;
	
	DBG("hw_ptr -> 0");
	data->hw_ptr=0;

	return 0;
}

static int snd_pcm_play_hw_params(snd_pcm_ioplug_t *io,
                    snd_pcm_hw_params_t *params)
{

	return 0;
}

static int snd_pcm_play_poll_descriptors_count(snd_pcm_ioplug_t *io)
{
		DBG("");
	    return 1;
}


static int snd_pcm_play_poll_descriptors(snd_pcm_ioplug_t *io,
                    struct pollfd *pfd, unsigned int space)
{
	DBG("");
	return 1;
}

static int snd_pcm_play_poll_revents(snd_pcm_ioplug_t *io,
                    struct pollfd *pfds, unsigned int nfds,
	                unsigned short *revents)
{
	DBG("");
	return 0;
}

static int snd_pcm_play_delay(snd_pcm_ioplug_t *io, snd_pcm_sframes_t
*delayp)
{
	DBG("");
	return 0;
}

static snd_pcm_ioplug_callback_t play_pcm_callback = {
    .close = snd_pcm_play_close,
    .start = snd_pcm_play_start,
    .stop = snd_pcm_play_stop,
    .pointer = snd_pcm_play_pointer,
    .transfer = snd_pcm_play_transfer,
	.prepare = snd_pcm_play_prepare,
	.hw_params = snd_pcm_play_hw_params,
	.poll_descriptors_count = snd_pcm_play_poll_descriptors_count,
	.poll_descriptors = snd_pcm_play_poll_descriptors,
	.poll_revents = snd_pcm_play_poll_revents,
	.delay = snd_pcm_play_delay,
};

static int play_hw_constraint(struct play_info * pcm)
{
	snd_pcm_ioplug_t *io = &pcm->io;


	static const snd_pcm_access_t access_list[] = {
		SND_PCM_ACCESS_RW_INTERLEAVED
	};
	static const unsigned int format_list[] = {
		SND_PCM_FORMAT_S16_LE,
	};

	int err;
	
	DBG("");

    /* access type */
    err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
                    ARRAY_SIZE(access_list), access_list);
    if (err < 0)
        return err;

    /* supported formats */
    err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
                    ARRAY_SIZE(format_list), format_list);
    if (err < 0)
        return err;

    /* supported channels */
    err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
                            1, 1);
    if (err < 0)
        return err;

    /* supported rate */
    err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,
                            8000, 8000);
    if (err < 0)
        return err;

    /* supported block size */
    err = snd_pcm_ioplug_set_param_minmax(io,
SND_PCM_IOPLUG_HW_PERIOD_BYTES,
                        156*2, 164*2);
    if (err < 0)
        return err;

    err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS,
                                    2, 200);
    if (err < 0)
        return err;

	return 0;
}

SND_PCM_PLUGIN_DEFINE_FUNC(play)
{
	snd_config_iterator_t i, next;
	const char *filename=NULL;
	int err;
	struct play_info *play=NULL;

	if (stream != SND_PCM_STREAM_PLAYBACK)
	{
		SNDERR("play plugin can only be used for playback\n");
		return -1;
	}
	
	snd_config_for_each(i, next, conf) {
		snd_config_t *n = snd_config_iterator_entry(i);
		const char *id;
		if (snd_config_get_id(n, &id) < 0)
			continue;

		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 ||
strcmp(id, "hint") == 0)
			continue;

		if (strcmp(id, "filename") == 0) {
			if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING)
			{
				SNDERR("invalid type for %s\n", id);
				return -EINVAL;
			}

			if (snd_config_get_string(n, &filename)<0)
			{
				SNDERR("could not get filename for %s\n", id);
				return -EINVAL;
			}
			continue;
		}

		SNDERR("Unknown field %s", id);
		return -EINVAL;
	}

	if (!filename)
	{
		SNDERR("no filename defined or play plugin\n");
		return -EINVAL;
	}

	if ((play = calloc(1, sizeof(*play))) == NULL)
		return -ENOMEM;

	play->io.version = SND_PCM_IOPLUG_VERSION;
	play->io.name = "ALSA play Plugin";
	play->io.callback = &play_pcm_callback;
	play->io.private_data = play;
	play->io.mmap_rw = 0;

	if ((err = snd_pcm_ioplug_create(&play->io, name, stream, mode))<0)
	{
        free(play);
        return err;
    }
	
	err = play_hw_constraint(play);
	if (err < 0) {
		snd_pcm_ioplug_delete(&play->io);
		free(play);
		return err;
	}
	
	*pcmp = play->io.pcm;

	return err;
}

SND_PCM_PLUGIN_SYMBOL(play);

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: io-plugin does not call start callback function
       [not found]   ` <358341420912040013m526a834bl968668da7b9f3c07@mail.gmail.com>
  2009-12-06 15:12     ` Stefan Schoenleitner
  2009-12-06 15:20     ` Stefan Schoenleitner
@ 2009-12-06 22:02     ` Stefan Schoenleitner
  2009-12-07  6:45       ` Raul Xiong
  2009-12-08 11:21       ` Takashi Iwai
  2 siblings, 2 replies; 8+ messages in thread
From: Stefan Schoenleitner @ 2009-12-06 22:02 UTC (permalink / raw)
  To: Raul Xiong; +Cc: alsa-devel@alsa-project.org

Hi,

Raul Xiong wrote:
> Hi Stefan
> 
> This is because your pointer callback function is not implemented properly.
> "pointer" callback function should return the right position of the
> playback/capture buffer, apps over alsa framework will call
> snd_pcm_avail_update to get the position.
> Also, you should call snd_pcm_ioplug_set_param_minmax in your
> play_hw_constraint function to define the buffer size relevant parameters.

after a full day of experimenting and trying to find out why the start
callback function is not called I observed something interesting:


It seems that for the io plugin there is an application pointer
io->appl_ptr and the hardware pointer io->hw_ptr.

Once playback is started, the start callback function is *not* called,
but instead data transfer is started immediately.
More precisely, each time the pointer callback function is called to get
the current position of the hardware pointer and after that the transfer
function is called to do the actual data transfer.

After each call of the transfer function the number of transfered frames
is returned (i.e. the number of frames that have been written to some
buffer we would like to fill behind a file descriptor).
However, as data transfers have to be timed precisely, we can not just
increment the hw_ptr from inside the transfer callback function.

If the hw_ptr is not touched at all (thus it stays 0 at all times), a
few more transfers are performed until the application pointer appl_ptr
has reached a certain (high) amount of "transfered" frames.

Then, which is most interesting, the start callback function *is* called
followed by calls to the pointer and polling/revent callback functions.
As I am not incrementing the hw_ptr at any place right now, the revent
polling function is called endlessly.



After a full day of even more experiments I think I know how it is
supposed to work:


In the beginning a certain amount of frames (I guess this is the
"start_threshold") is transfered (or more precisely prefilled in the
output buffer).
As soon as this start threshold is reached the start callback function
is called and alsa waits until the hardware pointer hw_ptr has reached a
certain position so that the next data transfers can be performed.
For this reason it seems that the hw_ptr can be seen as the pointer that
indicates how many frames already have been received (and maybe even
processed) at the receiving end.
Besides it also polls the filedescriptor(s) to check if the next data
transfers can be performed (without blocking actually).


At the moment I'm not sure if my assumptions above are close enough to
reality, but at least I think that I understand what is going on now.

If something like this would have been in the alsa documentation I guess
it would have saved me like a week of work.

cheers,
stefan

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: io-plugin does not call start callback function
  2009-12-06 22:02     ` Stefan Schoenleitner
@ 2009-12-07  6:45       ` Raul Xiong
  2009-12-07  7:18         ` Raul Xiong
  2009-12-08 11:21       ` Takashi Iwai
  1 sibling, 1 reply; 8+ messages in thread
From: Raul Xiong @ 2009-12-07  6:45 UTC (permalink / raw)
  To: Stefan Schoenleitner; +Cc: alsa-devel@alsa-project.org

Hi Stefan

What you said is basically right. I reviewed the code pcm.c in alsa-lib, it
shows the call procedure. But now I'm a little confused. In
snd_pcm_avail_update function, it computes with the pcm->hw.ptr value. And
in the plug-in we will write, only pcm->hw_ptr is updated. But who will link
these two value and when they are linking. I didn't find it.

2009/12/7 Stefan Schoenleitner <dev.c0debabe@gmail.com>

> Hi,
>
> Raul Xiong wrote:
> > Hi Stefan
> >
> > This is because your pointer callback function is not implemented
> properly.
> > "pointer" callback function should return the right position of the
> > playback/capture buffer, apps over alsa framework will call
> > snd_pcm_avail_update to get the position.
> > Also, you should call snd_pcm_ioplug_set_param_minmax in your
> > play_hw_constraint function to define the buffer size relevant
> parameters.
>
> after a full day of experimenting and trying to find out why the start
> callback function is not called I observed something interesting:
>
>
> It seems that for the io plugin there is an application pointer
> io->appl_ptr and the hardware pointer io->hw_ptr.
>
> Once playback is started, the start callback function is *not* called,
> but instead data transfer is started immediately.
> More precisely, each time the pointer callback function is called to get
> the current position of the hardware pointer and after that the transfer
> function is called to do the actual data transfer.
>
> After each call of the transfer function the number of transfered frames
> is returned (i.e. the number of frames that have been written to some
> buffer we would like to fill behind a file descriptor).
> However, as data transfers have to be timed precisely, we can not just
> increment the hw_ptr from inside the transfer callback function.
>
> If the hw_ptr is not touched at all (thus it stays 0 at all times), a
> few more transfers are performed until the application pointer appl_ptr
> has reached a certain (high) amount of "transfered" frames.
>
> Then, which is most interesting, the start callback function *is* called
> followed by calls to the pointer and polling/revent callback functions.
> As I am not incrementing the hw_ptr at any place right now, the revent
> polling function is called endlessly.
>
>
>
> After a full day of even more experiments I think I know how it is
> supposed to work:
>
>
> In the beginning a certain amount of frames (I guess this is the
> "start_threshold") is transfered (or more precisely prefilled in the
> output buffer).
> As soon as this start threshold is reached the start callback function
> is called and alsa waits until the hardware pointer hw_ptr has reached a
> certain position so that the next data transfers can be performed.
> For this reason it seems that the hw_ptr can be seen as the pointer that
> indicates how many frames already have been received (and maybe even
> processed) at the receiving end.
> Besides it also polls the filedescriptor(s) to check if the next data
> transfers can be performed (without blocking actually).
>
>
> At the moment I'm not sure if my assumptions above are close enough to
> reality, but at least I think that I understand what is going on now.
>
> If something like this would have been in the alsa documentation I guess
> it would have saved me like a week of work.
>
> cheers,
> stefan
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: io-plugin does not call start callback function
  2009-12-07  6:45       ` Raul Xiong
@ 2009-12-07  7:18         ` Raul Xiong
  0 siblings, 0 replies; 8+ messages in thread
From: Raul Xiong @ 2009-12-07  7:18 UTC (permalink / raw)
  To: Stefan Schoenleitner; +Cc: alsa-devel@alsa-project.org

I found it in snd_pcm_ioplug_create, it calls snd_pcm_set_hw_ptr to link
these two pointers. It's clear now.

2009/12/7 Raul Xiong <raulxiong@gmail.com>

> Hi Stefan
>
> What you said is basically right. I reviewed the code pcm.c in alsa-lib, it
> shows the call procedure. But now I'm a little confused. In
> snd_pcm_avail_update function, it computes with the pcm->hw.ptr value. And
> in the plug-in we will write, only pcm->hw_ptr is updated. But who will link
> these two value and when they are linking. I didn't find it.
>
> 2009/12/7 Stefan Schoenleitner <dev.c0debabe@gmail.com>
>
> Hi,
>>
>> Raul Xiong wrote:
>> > Hi Stefan
>> >
>> > This is because your pointer callback function is not implemented
>> properly.
>> > "pointer" callback function should return the right position of the
>> > playback/capture buffer, apps over alsa framework will call
>> > snd_pcm_avail_update to get the position.
>> > Also, you should call snd_pcm_ioplug_set_param_minmax in your
>> > play_hw_constraint function to define the buffer size relevant
>> parameters.
>>
>> after a full day of experimenting and trying to find out why the start
>> callback function is not called I observed something interesting:
>>
>>
>> It seems that for the io plugin there is an application pointer
>> io->appl_ptr and the hardware pointer io->hw_ptr.
>>
>> Once playback is started, the start callback function is *not* called,
>> but instead data transfer is started immediately.
>> More precisely, each time the pointer callback function is called to get
>> the current position of the hardware pointer and after that the transfer
>> function is called to do the actual data transfer.
>>
>> After each call of the transfer function the number of transfered frames
>> is returned (i.e. the number of frames that have been written to some
>> buffer we would like to fill behind a file descriptor).
>> However, as data transfers have to be timed precisely, we can not just
>> increment the hw_ptr from inside the transfer callback function.
>>
>> If the hw_ptr is not touched at all (thus it stays 0 at all times), a
>> few more transfers are performed until the application pointer appl_ptr
>> has reached a certain (high) amount of "transfered" frames.
>>
>> Then, which is most interesting, the start callback function *is* called
>> followed by calls to the pointer and polling/revent callback functions.
>> As I am not incrementing the hw_ptr at any place right now, the revent
>> polling function is called endlessly.
>>
>>
>>
>> After a full day of even more experiments I think I know how it is
>> supposed to work:
>>
>>
>> In the beginning a certain amount of frames (I guess this is the
>> "start_threshold") is transfered (or more precisely prefilled in the
>> output buffer).
>> As soon as this start threshold is reached the start callback function
>> is called and alsa waits until the hardware pointer hw_ptr has reached a
>> certain position so that the next data transfers can be performed.
>> For this reason it seems that the hw_ptr can be seen as the pointer that
>> indicates how many frames already have been received (and maybe even
>> processed) at the receiving end.
>> Besides it also polls the filedescriptor(s) to check if the next data
>> transfers can be performed (without blocking actually).
>>
>>
>> At the moment I'm not sure if my assumptions above are close enough to
>> reality, but at least I think that I understand what is going on now.
>>
>> If something like this would have been in the alsa documentation I guess
>> it would have saved me like a week of work.
>>
>> cheers,
>> stefan
>>
>
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: io-plugin does not call start callback function
  2009-12-06 22:02     ` Stefan Schoenleitner
  2009-12-07  6:45       ` Raul Xiong
@ 2009-12-08 11:21       ` Takashi Iwai
  1 sibling, 0 replies; 8+ messages in thread
From: Takashi Iwai @ 2009-12-08 11:21 UTC (permalink / raw)
  To: Stefan Schoenleitner; +Cc: alsa-devel@alsa-project.org, Raul Xiong

At Sun, 06 Dec 2009 23:02:00 +0100,
Stefan Schoenleitner wrote:
> 
> Hi,
> 
> Raul Xiong wrote:
> > Hi Stefan
> > 
> > This is because your pointer callback function is not implemented properly.
> > "pointer" callback function should return the right position of the
> > playback/capture buffer, apps over alsa framework will call
> > snd_pcm_avail_update to get the position.
> > Also, you should call snd_pcm_ioplug_set_param_minmax in your
> > play_hw_constraint function to define the buffer size relevant parameters.
> 
> after a full day of experimenting and trying to find out why the start
> callback function is not called I observed something interesting:
> 
> 
> It seems that for the io plugin there is an application pointer
> io->appl_ptr and the hardware pointer io->hw_ptr.
> 
> Once playback is started, the start callback function is *not* called,
> but instead data transfer is started immediately.
> More precisely, each time the pointer callback function is called to get
> the current position of the hardware pointer and after that the transfer
> function is called to do the actual data transfer.
> 
> After each call of the transfer function the number of transfered frames
> is returned (i.e. the number of frames that have been written to some
> buffer we would like to fill behind a file descriptor).
> However, as data transfers have to be timed precisely, we can not just
> increment the hw_ptr from inside the transfer callback function.
> 
> If the hw_ptr is not touched at all (thus it stays 0 at all times), a
> few more transfers are performed until the application pointer appl_ptr
> has reached a certain (high) amount of "transfered" frames.
> 
> Then, which is most interesting, the start callback function *is* called
> followed by calls to the pointer and polling/revent callback functions.
> As I am not incrementing the hw_ptr at any place right now, the revent
> polling function is called endlessly.
> 
> 
> 
> After a full day of even more experiments I think I know how it is
> supposed to work:
> 
> 
> In the beginning a certain amount of frames (I guess this is the
> "start_threshold") is transfered (or more precisely prefilled in the
> output buffer).
> As soon as this start threshold is reached the start callback function
> is called and alsa waits until the hardware pointer hw_ptr has reached a
> certain position so that the next data transfers can be performed.
> For this reason it seems that the hw_ptr can be seen as the pointer that
> indicates how many frames already have been received (and maybe even
> processed) at the receiving end.
> Besides it also polls the filedescriptor(s) to check if the next data
> transfers can be performed (without blocking actually).
> 
> 
> At the moment I'm not sure if my assumptions above are close enough to
> reality, but at least I think that I understand what is going on now.

Yeah, your guess is correct.
This "two pointers" concept is applied in general to all ALSA PCM core
(including the kernel driver).

> If something like this would have been in the alsa documentation I guess
> it would have saved me like a week of work.

What about contributing a patch for documentation?
It'd save another week of someone else ;)

Seriously, "better documented" can be done only if you know where and
how to improve.  Developers know already what's that and why this.
So, it's really appreciated if anyone else can check and update the
documents.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2009-12-08 11:21 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-12-03 11:49 io-plugin does not call start callback function Stefan Schoenleitner
2009-12-03 15:51 ` Stefan Schoenleitner
     [not found]   ` <358341420912040013m526a834bl968668da7b9f3c07@mail.gmail.com>
2009-12-06 15:12     ` Stefan Schoenleitner
2009-12-06 15:20     ` Stefan Schoenleitner
2009-12-06 22:02     ` Stefan Schoenleitner
2009-12-07  6:45       ` Raul Xiong
2009-12-07  7:18         ` Raul Xiong
2009-12-08 11:21       ` Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.