2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								/***
							 | 
						
					
						
							
								
									
										
										
										
											2006-06-19 21:53:48 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								  This file is part of PulseAudio.
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-02-13 15:35:19 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								  Copyright 2006 Lennart Poettering
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								  Copyright 2009 Finn Thain
							 | 
						
					
						
							
								
									
										
										
										
											2007-02-13 15:35:19 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-06-19 21:53:48 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								  PulseAudio is free software; you can redistribute it and/or modify
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								  it under the terms of the GNU Lesser General Public License as published
							 | 
						
					
						
							
								
									
										
										
										
											2009-03-03 20:23:02 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								  by the Free Software Foundation; either version 2.1 of the License,
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								  or (at your option) any later version.
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-06-19 21:53:48 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								  PulseAudio is distributed in the hope that it will be useful, but
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								  WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								  General Public License for more details.
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								  You should have received a copy of the GNU Lesser General Public License
							 | 
						
					
						
							
								
									
										
										
										
											2006-06-19 21:53:48 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								  along with PulseAudio; if not, write to the Free Software
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								  USA.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								***/
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#ifdef HAVE_CONFIG_H
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <config.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#endif
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <stdlib.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <stdio.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <errno.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <fcntl.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <unistd.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <sys/ioctl.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <sys/types.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#include <signal.h>
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <stropts.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <sys/conf.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <sys/audio.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-06-19 21:53:48 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#include <pulse/mainloop-signal.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <pulse/xmalloc.h>
							 | 
						
					
						
							
								
									
										
										
										
											2006-08-22 07:25:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#include <pulse/timeval.h>
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#include <pulse/util.h>
							 | 
						
					
						
							
								
									
										
										
										
											2009-06-25 11:47:23 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#include <pulse/rtclock.h>
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-16 19:19:58 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-06-19 21:53:48 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/sink.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/source.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/module.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/sample-util.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/core-util.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/modargs.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/log.h>
							 | 
						
					
						
							
								
									
										
										
										
											2006-08-22 07:21:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/core-error.h>
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/thread-mq.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/rtpoll.h>
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/thread.h>
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#include <pulsecore/time-smoother.h>
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-16 19:19:58 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#include "module-solaris-symdef.h"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								PA_MODULE_AUTHOR("Pierre Ossman");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								PA_MODULE_DESCRIPTION("Solaris Sink/Source");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								PA_MODULE_VERSION(PACKAGE_VERSION);
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-27 05:41:18 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								PA_MODULE_USAGE(
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "sink_name=<name for the sink> "
							 | 
						
					
						
							
								
									
										
										
										
											2009-05-28 02:39:22 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "sink_properties=<properties for the sink> "
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-27 05:41:18 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "source_name=<name for the source> "
							 | 
						
					
						
							
								
									
										
										
										
											2009-05-28 02:39:22 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "source_properties=<properties for the source> "
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "device=<audio device file name> "
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "record=<enable source?> "
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-27 05:41:18 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "playback=<enable sink?> "
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "format=<sample format> "
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "channels=<number of channels> "
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "rate=<sample rate> "
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "buffer_length=<milliseconds> "
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "channel_map=<channel map>");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								PA_MODULE_LOAD_ONCE(FALSE);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								struct userdata {
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_core *core;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-11 01:17:39 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_sink *sink;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_source *source;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_thread *thread;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_thread_mq thread_mq;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_rtpoll *rtpoll;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_signal_event *sig;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-08-22 16:25:47 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_memchunk memchunk;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 15:37:52 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    uint32_t frame_size;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    int32_t buffer_size;
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    uint64_t written_bytes, read_bytes;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    char *device_name;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    int mode;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    int fd;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_rtpoll_item *rtpoll_item;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-11 01:17:39 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_module *module;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_bool_t sink_suspended, source_suspended;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    uint32_t play_samples_msw, record_samples_msw;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    uint32_t prev_playback_samples, prev_record_samples;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    int32_t minimum_request;
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_smoother *smoother;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								};
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								static const char* const valid_modargs[] = {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "sink_name",
							 | 
						
					
						
							
								
									
										
										
										
											2009-05-28 02:39:22 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "sink_properties",
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "source_name",
							 | 
						
					
						
							
								
									
										
										
										
											2009-05-28 02:39:22 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "source_properties",
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "device",
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "record",
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "playback",
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "buffer_length",
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "format",
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "rate",
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    "channels",
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-27 05:41:18 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    "channel_map",
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    NULL
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								};
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								#define DEFAULT_DEVICE "/dev/audio"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#define MAX_RENDER_HZ   (300)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								/* This render rate limit imposes a minimum latency, but without it we waste too much CPU time. */
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								#define MAX_BUFFER_SIZE (128 * 1024)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								/* An attempt to buffer more than 128 KB causes write() to fail with errno == EAGAIN. */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static uint64_t get_playback_buffered_bytes(struct userdata *u) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    uint64_t played_bytes;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    int err;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u->sink);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    err = ioctl(u->fd, AUDIO_GETINFO, &info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(err >= 0);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /* Handle wrap-around of the device's sample counter, which is a uint_32. */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->prev_playback_samples > info.play.samples) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        /*
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								         * Unfortunately info.play.samples can sometimes go backwards, even before it wraps!
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								         * The bug seems to be absent on Solaris x86 nv117 with audio810 driver, at least on this (UP) machine.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								         * The bug is present on a different (SMP) machine running Solaris x86 nv103 with audioens driver.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								         * An earlier revision of this file mentions the same bug independently (unknown configuration).
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								         */
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (u->prev_playback_samples + info.play.samples < 240000) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ++u->play_samples_msw;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        } else {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log_debug("play.samples went backwards %d bytes", u->prev_playback_samples - info.play.samples);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->prev_playback_samples = info.play.samples;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    played_bytes = (((uint64_t)u->play_samples_msw << 32) + info.play.samples) * u->frame_size;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_smoother_put(u->smoother, pa_rtclock_now(), pa_bytes_to_usec(played_bytes, &u->sink->sample_spec));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2013-01-04 16:49:52 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->written_bytes > played_bytes)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return u->written_bytes - played_bytes;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return 0;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								static pa_usec_t sink_get_latency(struct userdata *u, pa_sample_spec *ss) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_usec_t r = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(ss);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->fd >= 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        r = pa_bytes_to_usec(get_playback_buffered_bytes(u), ss);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (u->memchunk.memblock)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            r += pa_bytes_to_usec(u->memchunk.length, ss);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    return r;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								static uint64_t get_recorded_bytes(struct userdata *u) {
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    uint64_t result;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    int err;
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u->source);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    err = ioctl(u->fd, AUDIO_GETINFO, &info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(err >= 0);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->prev_record_samples > info.record.samples)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        ++u->record_samples_msw;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->prev_record_samples = info.record.samples;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    result = (((uint64_t)u->record_samples_msw << 32) + info.record.samples) * u->frame_size;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    return result;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static pa_usec_t source_get_latency(struct userdata *u, pa_sample_spec *ss) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_usec_t r = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(ss);
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 15:37:52 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->fd) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        int err = ioctl(u->fd, AUDIO_GETINFO, &info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_assert(err >= 0);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        r = pa_bytes_to_usec(get_recorded_bytes(u), ss) - pa_bytes_to_usec(u->read_bytes, ss);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    return r;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static void build_pollfd(struct userdata *u) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct pollfd *pollfd;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(!u->rtpoll_item);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pollfd->fd = u->fd;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pollfd->events = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pollfd->revents = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 15:37:52 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static int set_buffer(int fd, int buffer_size) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_assert(fd >= 0);
							 | 
						
					
						
							
								
									
										
										
										
											2006-08-22 16:25:47 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    AUDIO_INITINFO(&info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    info.play.buffer_size = buffer_size;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    info.record.buffer_size = buffer_size;
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (errno == EINVAL)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: Unsupported buffer size.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    return 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 15:37:52 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static int auto_format(int fd, int mode, pa_sample_spec *ss) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(fd >= 0);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(ss);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    AUDIO_INITINFO(&info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (mode != O_RDONLY) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        info.play.sample_rate = ss->rate;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        info.play.channels = ss->channels;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        switch (ss->format) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SAMPLE_U8:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.play.precision = 8;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.play.encoding = AUDIO_ENCODING_LINEAR;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SAMPLE_ALAW:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.play.precision = 8;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.play.encoding = AUDIO_ENCODING_ALAW;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SAMPLE_ULAW:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.play.precision = 8;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.play.encoding = AUDIO_ENCODING_ULAW;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SAMPLE_S16NE:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.play.precision = 16;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.play.encoding = AUDIO_ENCODING_LINEAR;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        default:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: Unsupported sample format.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            return -1;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 15:37:52 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (mode != O_WRONLY) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        info.record.sample_rate = ss->rate;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        info.record.channels = ss->channels;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        switch (ss->format) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SAMPLE_U8:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.record.precision = 8;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.record.encoding = AUDIO_ENCODING_LINEAR;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SAMPLE_ALAW:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.record.precision = 8;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.record.encoding = AUDIO_ENCODING_ALAW;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SAMPLE_ULAW:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.record.precision = 8;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.record.encoding = AUDIO_ENCODING_ULAW;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SAMPLE_S16NE:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.record.precision = 16;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            info.record.encoding = AUDIO_ENCODING_LINEAR;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        default:
							 | 
						
					
						
							
								
									
										
										
										
											2011-03-02 12:41:26 +01:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: Unsupported sample format.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            return -1;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (errno == EINVAL)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: Failed to set sample format.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    return 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								static int open_audio_device(struct userdata *u, pa_sample_spec *ss) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(ss);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-11-20 21:22:24 +01:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if ((u->fd = pa_open_cloexec(u->device_name, u->mode | O_NONBLOCK, 0)) < 0) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_log_warn("open %s failed (%s)", u->device_name, pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_log_info("device opened in %s mode.", u->mode == O_WRONLY ? "O_WRONLY" : (u->mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (auto_format(u->fd, u->mode, ss) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (set_buffer(u->fd, u->buffer_size) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->written_bytes = u->read_bytes = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->play_samples_msw = u->record_samples_msw = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->prev_playback_samples = u->prev_record_samples = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    return u->fd;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								static int suspend(struct userdata *u) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u->fd >= 0);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_log_info("Suspending...");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2013-01-04 16:57:48 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    ioctl(u->fd, I_FLUSH, FLUSHRW);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_close(u->fd);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->fd = -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->rtpoll_item) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_rtpoll_item_free(u->rtpoll_item);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        u->rtpoll_item = NULL;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_log_info("Device suspended.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    return 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								static int unsuspend(struct userdata *u) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u->fd < 0);
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_log_info("Resuming...");
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (open_audio_device(u, u->sink ? &u->sink->sample_spec : &u->source->sample_spec) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    build_pollfd(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_log_info("Device resumed.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    return 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u = PA_SINK(o)->userdata;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    switch (code) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SINK_MESSAGE_GET_LATENCY:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            *((pa_usec_t*) data) = sink_get_latency(u, &PA_SINK(o)->sample_spec);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            return 0;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        case PA_SINK_MESSAGE_SET_STATE:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SINK_SUSPENDED:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    pa_smoother_pause(u->smoother, pa_rtclock_now());
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    if (!u->source || u->source_suspended) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        if (suspend(u) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                            return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    u->sink_suspended = TRUE;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SINK_IDLE:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SINK_RUNNING:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                        pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                        if (!u->source || u->source_suspended) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                            if (unsuspend(u) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                                return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                            u->sink->get_volume(u->sink);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                            u->sink->get_mute(u->sink);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        u->sink_suspended = FALSE;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SINK_INVALID_STATE:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SINK_UNLINKED:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SINK_INIT:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    ;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    return pa_sink_process_msg(o, code, data, offset, chunk);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u = PA_SOURCE(o)->userdata;
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    switch (code) {
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        case PA_SOURCE_MESSAGE_GET_LATENCY:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            *((pa_usec_t*) data) = source_get_latency(u, &PA_SOURCE(o)->sample_spec);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            return 0;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        case PA_SOURCE_MESSAGE_SET_STATE:
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
							 | 
						
					
						
							
								
									
										
										
										
											2006-08-23 07:57:43 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                case PA_SOURCE_SUSPENDED:
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    if (!u->sink || u->sink_suspended) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        if (suspend(u) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                            return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    u->source_suspended = TRUE;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SOURCE_IDLE:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SOURCE_RUNNING:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        if (!u->sink || u->sink_suspended) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                            if (unsuspend(u) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                                return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                            u->source->get_volume(u->source);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        u->source_suspended = FALSE;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SOURCE_UNLINKED:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SOURCE_INIT:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                case PA_SOURCE_INVALID_STATE:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    ;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            break;
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    return pa_source_process_msg(o, code, data, offset, chunk);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static void sink_set_volume(pa_sink *s) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert_se(u = s->userdata);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->fd >= 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        AUDIO_INITINFO(&info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:15:38 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        assert(info.play.gain <= AUDIO_MAX_GAIN);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (errno == EINVAL)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                pa_log("AUDIO_SETINFO: Unsupported volume.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static void sink_get_volume(pa_sink *s) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert_se(u = s->userdata);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->fd >= 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        else
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:15:38 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 14:30:42 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static void source_set_volume(pa_source *s) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 14:30:42 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_assert_se(u = s->userdata);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->fd >= 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        AUDIO_INITINFO(&info);
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 14:30:42 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2011-10-20 14:46:23 +05:30
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        assert(info.play.gain <= AUDIO_MAX_GAIN);
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 14:30:42 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (errno == EINVAL)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                pa_log("AUDIO_SETINFO: Unsupported volume.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 14:30:42 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static void source_get_volume(pa_source *s) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_assert_se(u = s->userdata);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-27 09:18:18 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->fd >= 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        else
							 | 
						
					
						
							
								
									
										
										
										
											2011-10-20 14:46:23 +05:30
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-27 09:20:25 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static void sink_set_mute(pa_sink *s) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u = s->userdata;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->fd >= 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        AUDIO_INITINFO(&info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        info.output_muted = !!s->muted;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								static void sink_get_mute(pa_sink *s) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u = s->userdata;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->fd >= 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            s->muted = !!info.output_muted;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static void process_rewind(struct userdata *u) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    size_t rewind_nbytes;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2012-08-30 16:50:13 +03:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_process_rewind(u->sink, 0);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (rewind_nbytes > 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        rewind_nbytes = PA_MIN(u->memchunk.length, rewind_nbytes);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        u->memchunk.length -= rewind_nbytes;
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-25 03:47:59 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (u->memchunk.length <= 0 && u->memchunk.memblock) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_memblock_unref(u->memchunk.memblock);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_memchunk_reset(&u->memchunk);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_sink_process_rewind(u->sink, rewind_nbytes);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static void thread_func(void *userdata) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u = userdata;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    unsigned short revents = 0;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    int ret, err;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    audio_info_t info;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_log_debug("Thread starting up");
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->core->realtime_scheduling)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_make_realtime(u->core->realtime_priority);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_thread_mq_install(&u->thread_mq);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    for (;;) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        /* Render some data and write it to the dsp */
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2012-11-16 23:09:15 +05:30
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
							 | 
						
					
						
							
								
									
										
										
										
											2012-08-30 16:50:13 +03:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            process_rewind(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pa_usec_t xtime0, ysleep_interval, xsleep_interval;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            uint64_t buffered_bytes;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            err = ioctl(u->fd, AUDIO_GETINFO, &info);
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            if (err < 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                pa_log("AUDIO_GETINFO ioctl failed: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            if (info.play.error) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                pa_log_debug("buffer under-run!");
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-20 16:13:10 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                AUDIO_INITINFO(&info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                info.play.error = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-20 16:13:10 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            for (;;) {
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                void *p;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                ssize_t w;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                size_t len;
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                int write_type = 1;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                /*
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                 * Since we cannot modify the size of the output buffer we fake it
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                 * by not filling it more than u->buffer_size.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                 */
							 | 
						
					
						
							
								
									
										
										
										
											2009-04-04 22:56:38 +03:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                xtime0 = pa_rtclock_now();
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                buffered_bytes = get_playback_buffered_bytes(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                if (buffered_bytes >= (uint64_t)u->buffer_size)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                len = u->buffer_size - buffered_bytes;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                len -= len % u->frame_size;
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-20 16:13:10 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                if (len < (size_t) u->minimum_request)
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    break;
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-20 16:13:10 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-25 03:47:59 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                if (!u->memchunk.length)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk);
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                len = PA_MIN(u->memchunk.length, len);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-20 16:13:10 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                p = pa_memblock_acquire(u->memchunk.memblock);
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, len, &write_type);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                pa_memblock_release(u->memchunk.memblock);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-20 16:13:10 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                if (w <= 0) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    if (errno == EINTR) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        continue;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    } else if (errno == EAGAIN) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        /* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    } else {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        goto fail;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                } else {
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    pa_assert(w % u->frame_size == 0);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-20 16:13:10 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    u->written_bytes += w;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    u->memchunk.index += w;
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    u->memchunk.length -= w;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    if (u->memchunk.length <= 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        pa_memblock_unref(u->memchunk.memblock);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        pa_memchunk_reset(&u->memchunk);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval));
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        } else
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pa_rtpoll_set_timer_disabled(u->rtpoll);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-24 10:17:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        /* Try to read some data and pass it on to the source driver */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && (revents & POLLIN)) {
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pa_memchunk memchunk;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            void *p;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ssize_t r;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            size_t len;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            err = ioctl(u->fd, AUDIO_GETINFO, &info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_assert(err >= 0);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (info.record.error) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                pa_log_debug("buffer overflow!");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                AUDIO_INITINFO(&info);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                info.record.error = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            err = ioctl(u->fd, I_NREAD, &len);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pa_assert(err >= 0);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            if (len > 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                memchunk.memblock = pa_memblock_new(u->core->mempool, len);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                pa_assert(memchunk.memblock);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                p = pa_memblock_acquire(memchunk.memblock);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                r = pa_read(u->fd, p, len, NULL);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                pa_memblock_release(memchunk.memblock);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                if (r < 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    pa_memblock_unref(memchunk.memblock);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    if (errno == EAGAIN)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        break;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    else {
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                } else {
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    u->read_bytes += r;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                    memchunk.index = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    memchunk.length = r;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    pa_source_post(u->source, &memchunk);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    pa_memblock_unref(memchunk.memblock);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    revents &= ~POLLIN;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-24 10:17:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (u->rtpoll_item) {
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            struct pollfd *pollfd;
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-24 10:17:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pa_assert(u->fd >= 0);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pollfd->events = (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-24 10:17:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        /* Hmm, nothing to do. Let's sleep */
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            goto fail;
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-24 10:17:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (ret == 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            goto finish;
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-24 10:17:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (u->rtpoll_item) {
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            struct pollfd *pollfd;
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-24 10:17:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-24 10:17:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                pa_log("DSP shutdown.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-23 09:44:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            revents = pollfd->revents;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        } else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            revents = 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-23 09:44:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								fail:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /* We have to continue processing messages until we receive the
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * SHUTDOWN message */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-23 09:44:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								finish:
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_log_debug("Thread shutting down");
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-23 09:44:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u = userdata;
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-23 09:44:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    assert(u);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-23 09:44:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_log_debug("caught signal");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->sink) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:15:38 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_sink_get_volume(u->sink, TRUE);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_sink_get_mute(u->sink, TRUE);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-23 09:44:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->source)
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_source_get_volume(u->source, TRUE);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								int pa__init(pa_module *m) {
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u = NULL;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_bool_t record = TRUE, playback = TRUE;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-11 01:17:39 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_sample_spec ss;
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-27 05:41:18 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_channel_map map;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-11 01:17:39 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_modargs *ma = NULL;
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    uint32_t buffer_length_msec;
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    int fd = -1;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_sink_new_data sink_new_data;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_source_new_data source_new_data;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    char const *name;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    char *name_buf;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_bool_t namereg_fail;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(m);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
							 | 
						
					
						
							
								
									
										
										
										
											2006-08-18 21:38:40 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_log("failed to parse module arguments.");
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_log("record= and playback= expect a boolean argument.");
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (!playback && !record) {
							 | 
						
					
						
							
								
									
										
										
										
											2006-08-18 21:38:40 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_log("neither playback nor record enabled for device.");
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-03-04 02:52:11 +01:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    u = pa_xnew0(struct userdata, 1);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (!(u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC * 2, TRUE, TRUE, 10, pa_rtclock_now(), TRUE)))
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    /*
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * For a process (or several processes) to use the same audio device for both
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * record and playback at the same time, the device's mixer must be enabled.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * See mixerctl(1). It may be turned off for playback only or record only.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    ss = m->core->default_sample_spec;
							 | 
						
					
						
							
								
									
										
										
										
											2006-05-17 06:31:18 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
							 | 
						
					
						
							
								
									
										
										
										
											2006-08-18 21:38:40 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_log("failed to parse sample specification");
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    u->frame_size = pa_frame_size(&ss);
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    u->minimum_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, &ss);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    buffer_length_msec = 100;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (pa_modargs_get_value_u32(ma, "buffer_length", &buffer_length_msec) < 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_log("failed to parse buffer_length argument");
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    u->buffer_size = pa_usec_to_bytes(1000 * buffer_length_msec, &ss);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->buffer_size < 2 * u->minimum_request) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_log("buffer_length argument cannot be smaller than %u",
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								               (unsigned)(pa_bytes_to_usec(2 * u->minimum_request, &ss) / 1000));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->buffer_size > MAX_BUFFER_SIZE) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_log("buffer_length argument cannot be greater than %u",
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								               (unsigned)(pa_bytes_to_usec(MAX_BUFFER_SIZE, &ss) / 1000));
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->device_name = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if ((fd = open_audio_device(u, &ss)) < 0)
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    u->core = m->core;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->module = m;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    m->userdata = u;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_memchunk_reset(&u->memchunk);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    u->rtpoll = pa_rtpoll_new();
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    u->rtpoll_item = NULL;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    build_pollfd(u);
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 14:30:42 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->mode != O_WRONLY) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        name_buf = NULL;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        namereg_fail = TRUE;
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 14:30:42 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (!(name = pa_modargs_get_value(ma, "source_name", NULL))) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            name = name_buf = pa_sprintf_malloc("solaris_input.%s", pa_path_get_filename(u->device_name));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            namereg_fail = FALSE;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_source_new_data_init(&source_new_data);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        source_new_data.driver = __FILE__;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        source_new_data.module = m;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_source_new_data_set_name(&source_new_data, name);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        source_new_data.namereg_fail = namereg_fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_source_new_data_set_sample_spec(&source_new_data, &ss);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_source_new_data_set_channel_map(&source_new_data, &map);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "solaris");
							 | 
						
					
						
							
								
									
										
										
										
											2009-05-28 02:39:22 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Solaris PCM source");
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "serial");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) u->buffer_size);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-05-28 02:39:22 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (pa_modargs_get_proplist(ma, "source_properties", source_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("Invalid properties");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_source_new_data_done(&source_new_data);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2011-07-05 23:44:06 +01:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_source_new_data_done(&source_new_data);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_xfree(name_buf);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (!u->source) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("Failed to create source object");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        u->source->userdata = u;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        u->source->parent.process_msg = source_process_msg;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_source_set_rtpoll(u->source, u->rtpoll);
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->source->sample_spec));
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2011-07-17 15:29:29 +01:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_source_set_get_volume_callback(u->source, source_get_volume);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_source_set_set_volume_callback(u->source, source_set_volume);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        u->source->refresh_volume = TRUE;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    } else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        u->source = NULL;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->mode != O_RDONLY) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        name_buf = NULL;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        namereg_fail = TRUE;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (!(name = pa_modargs_get_value(ma, "sink_name", NULL))) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            name = name_buf = pa_sprintf_malloc("solaris_output.%s", pa_path_get_filename(u->device_name));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            namereg_fail = FALSE;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_new_data_init(&sink_new_data);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        sink_new_data.driver = __FILE__;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        sink_new_data.module = m;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_new_data_set_name(&sink_new_data, name);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        sink_new_data.namereg_fail = namereg_fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_new_data_set_channel_map(&sink_new_data, &map);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "solaris");
							 | 
						
					
						
							
								
									
										
										
										
											2009-05-28 02:39:22 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Solaris PCM sink");
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "serial");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-05-28 02:39:22 +02:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (pa_modargs_get_proplist(ma, "sink_properties", sink_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_log("Invalid properties");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pa_sink_new_data_done(&sink_new_data);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2011-07-05 23:44:06 +01:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_sink_new_data_done(&sink_new_data);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_assert(u->sink);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        u->sink->userdata = u;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        u->sink->parent.process_msg = sink_process_msg;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_set_rtpoll(u->sink, u->rtpoll);
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:18:40 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->buffer_size, &u->sink->sample_spec));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_set_max_request(u->sink, u->buffer_size);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_set_max_rewind(u->sink, u->buffer_size);
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2011-07-17 15:29:29 +01:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_sink_set_get_volume_callback(u->sink, sink_get_volume);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_set_get_mute_callback(u->sink, sink_get_mute);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_set_set_mute_callback(u->sink, sink_set_mute);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        u->sink->refresh_volume = u->sink->refresh_muted = TRUE;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    } else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        u->sink = NULL;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(u->source || u->sink);
							 | 
						
					
						
							
								
									
										
										
										
											2006-04-19 14:30:42 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->sig)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        ioctl(u->fd, I_SETSIG, S_MSG);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_log_warn("Could not register SIGPOLL handler");
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2011-02-22 11:04:19 -05:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (!(u->thread = pa_thread_new("solaris", thread_func, u))) {
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_log("Failed to create thread.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        goto fail;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-23 09:07:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    /* Read mixer settings */
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-24 10:17:41 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->sink) {
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (sink_new_data.volume_is_set)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            u->sink->set_volume(u->sink);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            u->sink->get_volume(u->sink);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (sink_new_data.muted_is_set)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            u->sink->set_mute(u->sink);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            u->sink->get_mute(u->sink);
							 | 
						
					
						
							
								
									
										
										
										
											2006-02-23 09:07:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_sink_put(u->sink);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->source) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (source_new_data.volume_is_set)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            u->source->set_volume(u->source);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        else
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            u->source->get_volume(u->source);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_source_put(u->source);
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_modargs_free(ma);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    return 0;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								fail:
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa__done(m);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    else if (fd >= 0)
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        close(fd);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (ma)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_modargs_free(ma);
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    return -1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								void pa__done(pa_module *m) {
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    struct userdata *u;
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_assert(m);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (!(u = m->userdata))
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return;
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-19 10:24:49 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> >  struct userdata {
> >      pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> >      pa_memchunk memchunk;
> >
> > -    unsigned int page_size;
> > -
> >      uint32_t frame_size;
> > -    uint32_t buffer_size;
> > -    unsigned int written_bytes, read_bytes;
> > +    int32_t buffer_size;
> > +    volatile uint64_t written_bytes, read_bytes;
> > +    pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > +    struct userdata *u;
> > +    audio_info_t info;
> > +
> > +    pa_assert_se(u = s->userdata);
> > +
> > +    if (u->fd >= 0) {
> > +        AUDIO_INITINFO(&info);
> > +
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > -    info.play.error = 0;
> > +        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > +        assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > +            if (u->sink->thread_info.rewind_requested)
> > +                pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> >              err = ioctl(u->fd, AUDIO_GETINFO, &info);
> >              pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > +                        case EINTR:
> > +                            break;
>
> I think you should simply try again in this case...
Done.
> > +                        case EAGAIN:
> > +                            u->buffer_size = u->buffer_size * 18 / 25;
> > +                            u->buffer_size -= u->buffer_size % u->frame_size;
> > +                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > +                            pa_sink_set_max_request(u->sink, u->buffer_size);
> > +                            pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > +                            break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > +            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > +        } else {
> > +            pa_rtpoll_set_timer_disabled(u->rtpoll);
> >          }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > +    u->frame_size = pa_frame_size(&ss);
> >
> > -    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > +    u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
											
										 
										
											2009-03-07 16:48:10 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->sig) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        ioctl(u->fd, I_SETSIG, 0);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_signal_free(u->sig);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->sink)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_unlink(u->sink);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->source)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_source_unlink(u->source);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->thread) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_thread_free(u->thread);
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_thread_mq_done(&u->thread_mq);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->sink)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_sink_unref(u->sink);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->source)
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_source_unref(u->source);
							 | 
						
					
						
							
								
									
										
										
										
											2007-01-04 13:43:45 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->memchunk.memblock)
							 | 
						
					
						
							
								
									
										
										
										
											2007-10-28 19:13:50 +00:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        pa_memblock_unref(u->memchunk.memblock);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->rtpoll_item)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_rtpoll_item_free(u->rtpoll_item);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->rtpoll)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_rtpoll_free(u->rtpoll);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    if (u->fd >= 0)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        close(u->fd);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-08-21 13:17:03 +10:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    if (u->smoother)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        pa_smoother_free(u->smoother);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2009-02-26 16:48:58 +11:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    pa_xfree(u->device_name);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2006-01-10 17:51:06 +00:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    pa_xfree(u);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 |