Ticket #26 (closed enhancement: fixed)

Opened 3 years ago

Last modified 2 years ago

Support for nice suspend

Reported by: art@… Owned by: lennart
Milestone: Component: daemon
Keywords: Cc:

Description

After resuming from suspending to sleep or disk, pulse has a hard time regaining where it was, in part from the alsa devices ceasing to exist for a bit. It would be nice to be able to tell pulse that "the system is suspending" so that it has a chance to prepare, and "the system has resumed" to try and re-obtain the sinks. Perhaps a hal modules or a command that can be called?

Attachments

pulseaudio-0.9.5-suspend.patch (1.3 kB) - added by ranma 2 years ago.
Simple patch for suspend issue in alsa-sink

Change History

Changed 3 years ago by ossman

Even nicer would be if it could gracefully handle any kind of intermittent ALSA failure.

Changed 3 years ago by lennart

ossman: r1194 deals with ALSA failures. If reading/writing from/to ALSA fails we now simply unload the module. I added this to properly handle hot removing of USB sound cards. Seems to work quite well now.

Changed 3 years ago by cjvdb

I've experimented with suspend and pulseaudio quite a bit. I'm using s2disk from the new uswsusp package. This is basically what works for me:

before suspend:

load-module module-null-sink
set-default-sink {null-sink-id}

Now I suspend, and sometime later resume. During the suspend/resume cycle the alsa devices are forced closed by the kernel (or perhaps the temorarily disappear, not sure) and pa reacts by unloading the modules. When this happens pa rescues all streams to the null sink. This works great.

after resume:

load-module module-hal-detect
set-default-sink {id-of-first-alsa-sink}
unload-module {id-of-module-null-sink}

This all works very well and gives me seamless audio output across suspends (at least from the application's perspective) the only tricky bit is figuring out the ids from scripts.

Changed 3 years ago by ossman

I believe people are moving towards using dbus to notify applications of an imminent suspend. I don't know if this can solve all of our problems, but a module that listens for such events should have a better chance of doing the right thing.

Changed 3 years ago by ranma

Wouldn't it be possible for the alsa module just to reopen the device?

I'm also seeing this problem:

After s2disk:

[with mplayer using the alsa plugin] *** PULSEAUDIO: Unable to create stream. [AO_ALSA] Unable to set hw-parameters: Input/output error

[with paplay] ranma@melchior:~/projects/gbsplay-cvs$ paplay ~/projects/mediatestfiles/test.wav Stream errror: Invalid argument ranma@melchior:~/projects/gbsplay-cvs$ su Password: melchior:/home/ranma/projects/gbsplay-cvs# /etc/init.d/pulseaudio restart Stopping PulseAudio Daemon: pulseaudio. Starting PulseAudio Daemon: pulseaudiochown: cannot access `/var/run/pulse/.esd_auth': No such file or directory melchior:/home/ranma/projects/gbsplay-cvs# exit ranma@melchior:~/projects/gbsplay-cvs$ paplay ~/projects/mediatestfiles/test.wav

Changed 3 years ago by ranma

BTW, the following minimal ALSA test program survives s2disk just fine:

|ranma@melchior:~$ ./alsa-s2disk hw:0,0 |Opening ALSA PCM device 'hw:0,0'... |Open successfull, playing saw wave... |snd_pcm_writei failed: Streams pipe error [above error after resume once, but continues to play fine]

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <alsa/asoundlib.h>

snd_pcm_t *pcm_handle;

int main(int argc, char **argv) {

char *pcm_name = "default"; int err, i; unsigned exact_rate; unsigned rate = 48000; snd_pcm_hw_params_t *hwparams; short buf[8192 * 2];

if (argc == 2) {

pcm_name = argv[1];

}

printf("Opening ALSA PCM device '%s'...\n", pcm_name); snd_pcm_hw_params_alloca(&hwparams);

if ((err = snd_pcm_open(&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {

fprintf(stderr, "Could not open ALSA PCM device '%s': %s\n", pcm_name, snd_strerror(err)); return 1;

}

if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {

fprintf(stderr, "snd_pcm_hw_params_any failed: %s\n", snd_strerror(err)); return 1;

}

if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {

fprintf(stderr, "snd_pcm_hw_params_set_access failed: %s\n", snd_strerror(err)); return 1;

}

if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0) {

fprintf(stderr, "snd_pcm_hw_params_set_format failed: %s\n", snd_strerror(err)); return 1;

}

exact_rate = rate; if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0)) < 0) {

fprintf(stderr, "snd_pcm_hw_params_set_rate_near failed: %s\n", snd_strerror(err)); return 1;

} if (rate != exact_rate) {

fprintf(stderr, "Requested rate %ldHz, got %dHz.\n",

rate, exact_rate);

}

if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2)) < 0) {

fprintf(stderr, "snd_pcm_hw_params_set_channels failed: %s\n", snd_strerror(err)); return 1;

}

if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, 4, 0)) < 0) {

fprintf(stderr, "snd_pcm_hw_params_set_periods failed: %s\n", snd_strerror(err));

return 1;

}

if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, 8192)) < 0) {

fprintf(stderr, "snd_pcm_hw_params_set_buffer_size failed: %s\n", snd_strerror(err)); return 1;

}

if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {

fprintf(stderr, "snd_pcm_hw_params failed: %s\n", snd_strerror(err)); return 1;

}

printf("Open successfull, playing saw wave...\n", pcm_name);

for (i=0; i<8192*2; i+=2) {

short x = (i << 8) | (i & 0xff); buf[i] = x; buf[i+1] = x;

}

while (1) {

if ((err = snd_pcm_writei(pcm_handle, buf, 8192)) < 0) {

fprintf(stderr, "snd_pcm_writei failed: %s\n", snd_strerror(err)); snd_pcm_prepare(pcm_handle);

}

}

snd_pcm_drop(pcm_handle); snd_pcm_close(pcm_handle);

return 0;

}

Changed 3 years ago by ranma

Let's try this again with appropriate WikiFormatting, shall we?

BTW, the following minimal ALSA test program survives s2disk just fine:

|ranma@melchior:~/projects$ ./alsa-s2disk hw:0,0
|Opening ALSA PCM device 'hw:0,0'...
|Open successfull, playing saw wave...
|snd_pcm_writei failed: Streams pipe error

[Only one error message after resume, but continues to play fine]

Source code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <alsa/asoundlib.h>

snd_pcm_t *pcm_handle;

int main(int argc, char **argv)
{
	char *pcm_name = "default";
	int err, i;
	unsigned exact_rate;
	unsigned rate = 48000;
	snd_pcm_hw_params_t *hwparams;
	short buf[8192 * 2];

	if (argc == 2) {
		pcm_name = argv[1];
	}

	printf("Opening ALSA PCM device '%s'...\n", pcm_name);
	snd_pcm_hw_params_alloca(&hwparams);

	if ((err = snd_pcm_open(&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
		fprintf(stderr, "Could not open ALSA PCM device '%s': %s\n", pcm_name, snd_strerror(err));
		return 1;
	}

	if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
		fprintf(stderr, "snd_pcm_hw_params_any failed: %s\n", snd_strerror(err));
		return 1;
	}

	if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
		fprintf(stderr, "snd_pcm_hw_params_set_access failed: %s\n", snd_strerror(err));
		return 1;
	}

	if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0) {
		fprintf(stderr, "snd_pcm_hw_params_set_format failed: %s\n", snd_strerror(err));
		return 1;
	}

	exact_rate = rate;
	if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0)) < 0) {
		fprintf(stderr, "snd_pcm_hw_params_set_rate_near failed: %s\n", snd_strerror(err));
		return 1;
	}
	if (rate != exact_rate) {
		fprintf(stderr, "Requested rate %ldHz, got %dHz.\n",
		        rate, exact_rate);
	}

	if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2)) < 0) {
		fprintf(stderr, "snd_pcm_hw_params_set_channels failed: %s\n", snd_strerror(err));
		return 1;
	}

	if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, 4, 0)) < 0) {
	      fprintf(stderr, "snd_pcm_hw_params_set_periods failed: %s\n", snd_strerror(err));
		return 1;
	}

	if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, 8192)) < 0) {
		fprintf(stderr, "snd_pcm_hw_params_set_buffer_size failed: %s\n", snd_strerror(err));
		return 1;
	}

	if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
		fprintf(stderr, "snd_pcm_hw_params failed: %s\n", snd_strerror(err));
		return 1;
	}

	printf("Open successfull, playing saw wave...\n", pcm_name);

	for (i=0; i<8192*2; i+=2) {
		short x = (i << 8) | (i & 0xff);
		buf[i]   = x;
		buf[i+1] = x;
	}

	while (1) {
		if ((err = snd_pcm_writei(pcm_handle, buf, 8192)) < 0) {
			fprintf(stderr, "snd_pcm_writei failed: %s\n", snd_strerror(err));
			snd_pcm_prepare(pcm_handle);
		}
	}

	snd_pcm_drop(pcm_handle);
	snd_pcm_close(pcm_handle);

	return 0;
}

Changed 2 years ago by ranma

BTW, this is also documented on the alsa site, see the explanation for -ESTRPIPE and SND_PCM_STATE_SUSPENDED on http://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html

Changed 2 years ago by ranma

Simple patch for suspend issue in alsa-sink

Changed 2 years ago by ossman

  • status changed from new to closed
  • resolution set to fixed

(In [1433]) Handle suspended alsa devices. Based on patch by ranma. (closes #26)

Note: See TracTickets for help on using tickets.