2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <pulse/xmalloc.h>
32 #include <pulsecore/log.h>
33 #include <pulsecore/mcalign.h>
34 #include <pulsecore/macro.h>
35 #include <pulsecore/flist.h>
37 #include "memblockq.h"
39 /* #define MEMBLOCKQ_DEBUG */
42 struct list_item
*next
, *prev
;
47 PA_STATIC_FLIST_DECLARE(list_items
, 0, pa_xfree
);
50 struct list_item
*blocks
, *blocks_tail
;
51 struct list_item
*current_read
, *current_write
;
53 size_t maxlength
, tlength
, base
, prebuf
, minreq
, maxrewind
;
54 int64_t read_index
, write_index
;
58 int64_t missing
, requested
;
60 pa_sample_spec sample_spec
;
63 pa_memblockq
* pa_memblockq_new(
68 const pa_sample_spec
*sample_spec
,
72 pa_memchunk
*silence
) {
76 pa_assert(sample_spec
);
79 bq
= pa_xnew(pa_memblockq
, 1);
80 bq
->name
= pa_xstrdup(name
);
81 bq
->blocks
= bq
->blocks_tail
= NULL
;
82 bq
->current_read
= bq
->current_write
= NULL
;
85 bq
->sample_spec
= *sample_spec
;
86 bq
->base
= pa_frame_size(sample_spec
);
87 bq
->read_index
= bq
->write_index
= idx
;
89 pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
90 (unsigned long) maxlength
, (unsigned long) tlength
, (unsigned long) bq
->base
, (unsigned long) prebuf
, (unsigned long) minreq
, (unsigned long) maxrewind
);
92 bq
->missing
= bq
->requested
= 0;
93 bq
->maxlength
= bq
->tlength
= bq
->prebuf
= bq
->minreq
= bq
->maxrewind
= 0;
96 pa_memblockq_set_maxlength(bq
, maxlength
);
97 pa_memblockq_set_tlength(bq
, tlength
);
98 pa_memblockq_set_minreq(bq
, minreq
);
99 pa_memblockq_set_prebuf(bq
, prebuf
);
100 pa_memblockq_set_maxrewind(bq
, maxrewind
);
102 pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
103 (unsigned long) bq
->maxlength
, (unsigned long) bq
->tlength
, (unsigned long) bq
->base
, (unsigned long) bq
->prebuf
, (unsigned long) bq
->minreq
, (unsigned long) bq
->maxrewind
);
106 bq
->silence
= *silence
;
107 pa_memblock_ref(bq
->silence
.memblock
);
109 pa_memchunk_reset(&bq
->silence
);
111 bq
->mcalign
= pa_mcalign_new(bq
->base
);
116 void pa_memblockq_free(pa_memblockq
* bq
) {
119 pa_memblockq_silence(bq
);
121 if (bq
->silence
.memblock
)
122 pa_memblock_unref(bq
->silence
.memblock
);
125 pa_mcalign_free(bq
->mcalign
);
131 static void fix_current_read(pa_memblockq
*bq
) {
134 if (PA_UNLIKELY(!bq
->blocks
)) {
135 bq
->current_read
= NULL
;
139 if (PA_UNLIKELY(!bq
->current_read
))
140 bq
->current_read
= bq
->blocks
;
143 while (PA_UNLIKELY(bq
->current_read
->index
> bq
->read_index
))
145 if (bq
->current_read
->prev
)
146 bq
->current_read
= bq
->current_read
->prev
;
151 while (PA_LIKELY(bq
->current_read
!= NULL
) && PA_UNLIKELY(bq
->current_read
->index
+ (int64_t) bq
->current_read
->chunk
.length
<= bq
->read_index
))
152 bq
->current_read
= bq
->current_read
->next
;
154 /* At this point current_read will either point at or left of the
155 next block to play. It may be NULL in case everything in
156 the queue was already played */
159 static void fix_current_write(pa_memblockq
*bq
) {
162 if (PA_UNLIKELY(!bq
->blocks
)) {
163 bq
->current_write
= NULL
;
167 if (PA_UNLIKELY(!bq
->current_write
))
168 bq
->current_write
= bq
->blocks_tail
;
171 while (PA_UNLIKELY(bq
->current_write
->index
+ (int64_t) bq
->current_write
->chunk
.length
<= bq
->write_index
))
173 if (bq
->current_write
->next
)
174 bq
->current_write
= bq
->current_write
->next
;
179 while (PA_LIKELY(bq
->current_write
!= NULL
) && PA_UNLIKELY(bq
->current_write
->index
> bq
->write_index
))
180 bq
->current_write
= bq
->current_write
->prev
;
182 /* At this point current_write will either point at or right of
183 the next block to write data to. It may be NULL in case
184 everything in the queue is still to be played */
187 static void drop_block(pa_memblockq
*bq
, struct list_item
*q
) {
191 pa_assert(bq
->n_blocks
>= 1);
194 q
->prev
->next
= q
->next
;
196 pa_assert(bq
->blocks
== q
);
197 bq
->blocks
= q
->next
;
201 q
->next
->prev
= q
->prev
;
203 pa_assert(bq
->blocks_tail
== q
);
204 bq
->blocks_tail
= q
->prev
;
207 if (bq
->current_write
== q
)
208 bq
->current_write
= q
->prev
;
210 if (bq
->current_read
== q
)
211 bq
->current_read
= q
->next
;
213 pa_memblock_unref(q
->chunk
.memblock
);
215 if (pa_flist_push(PA_STATIC_FLIST_GET(list_items
), q
) < 0)
221 static void drop_backlog(pa_memblockq
*bq
) {
225 boundary
= bq
->read_index
- (int64_t) bq
->maxrewind
;
227 while (bq
->blocks
&& (bq
->blocks
->index
+ (int64_t) bq
->blocks
->chunk
.length
<= boundary
))
228 drop_block(bq
, bq
->blocks
);
231 static pa_bool_t
can_push(pa_memblockq
*bq
, size_t l
) {
236 if (bq
->read_index
> bq
->write_index
) {
237 int64_t d
= bq
->read_index
- bq
->write_index
;
245 end
= bq
->blocks_tail
? bq
->blocks_tail
->index
+ (int64_t) bq
->blocks_tail
->chunk
.length
: bq
->write_index
;
247 /* Make sure that the list doesn't get too long */
248 if (bq
->write_index
+ (int64_t) l
> end
)
249 if (bq
->write_index
+ (int64_t) l
- bq
->read_index
> (int64_t) bq
->maxlength
)
255 static void write_index_changed(pa_memblockq
*bq
, int64_t old_write_index
, pa_bool_t account
) {
260 delta
= bq
->write_index
- old_write_index
;
263 bq
->requested
-= delta
;
265 bq
->missing
-= delta
;
267 #ifdef MEMBLOCKQ_DEBUG
268 pa_log("[%s] pushed/seeked %lli: requested counter at %lli, account=%i", bq
->name
, (long long) delta
, (long long) bq
->requested
, account
);
272 static void read_index_changed(pa_memblockq
*bq
, int64_t old_read_index
) {
277 delta
= bq
->read_index
- old_read_index
;
278 bq
->missing
+= delta
;
280 #ifdef MEMBLOCKQ_DEBUG
281 pa_log("[%s] popped %lli: missing counter at %lli", bq
->name
, (long long) delta
, (long long) bq
->missing
);
285 int pa_memblockq_push(pa_memblockq
* bq
, const pa_memchunk
*uchunk
) {
286 struct list_item
*q
, *n
;
292 pa_assert(uchunk
->memblock
);
293 pa_assert(uchunk
->length
> 0);
294 pa_assert(uchunk
->index
+ uchunk
->length
<= pa_memblock_get_length(uchunk
->memblock
));
296 if (uchunk
->length
% bq
->base
)
299 if (!can_push(bq
, uchunk
->length
))
302 old
= bq
->write_index
;
305 fix_current_write(bq
);
306 q
= bq
->current_write
;
308 /* First we advance the q pointer right of where we want to
312 while (bq
->write_index
+ (int64_t) chunk
.length
> q
->index
)
322 /* We go from back to front to look for the right place to add
323 * this new entry. Drop data we will overwrite on the way */
327 if (bq
->write_index
>= q
->index
+ (int64_t) q
->chunk
.length
)
328 /* We found the entry where we need to place the new entry immediately after */
330 else if (bq
->write_index
+ (int64_t) chunk
.length
<= q
->index
) {
331 /* This entry isn't touched at all, let's skip it */
333 } else if (bq
->write_index
<= q
->index
&&
334 bq
->write_index
+ (int64_t) chunk
.length
>= q
->index
+ (int64_t) q
->chunk
.length
) {
336 /* This entry is fully replaced by the new entry, so let's drop it */
342 } else if (bq
->write_index
>= q
->index
) {
343 /* The write index points into this memblock, so let's
344 * truncate or split it */
346 if (bq
->write_index
+ (int64_t) chunk
.length
< q
->index
+ (int64_t) q
->chunk
.length
) {
348 /* We need to save the end of this memchunk */
352 /* Create a new list entry for the end of the memchunk */
353 if (!(p
= pa_flist_pop(PA_STATIC_FLIST_GET(list_items
))))
354 p
= pa_xnew(struct list_item
, 1);
357 pa_memblock_ref(p
->chunk
.memblock
);
359 /* Calculate offset */
360 d
= (size_t) (bq
->write_index
+ (int64_t) chunk
.length
- q
->index
);
363 /* Drop it from the new entry */
364 p
->index
= q
->index
+ (int64_t) d
;
365 p
->chunk
.length
-= d
;
367 /* Add it to the list */
369 if ((p
->next
= q
->next
))
378 /* Truncate the chunk */
379 if (!(q
->chunk
.length
= (size_t) (bq
->write_index
- q
->index
))) {
386 /* We had to truncate this block, hence we're now at the right position */
391 pa_assert(bq
->write_index
+ (int64_t)chunk
.length
> q
->index
&&
392 bq
->write_index
+ (int64_t)chunk
.length
< q
->index
+ (int64_t)q
->chunk
.length
&&
393 bq
->write_index
< q
->index
);
395 /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
397 d
= (size_t) (bq
->write_index
+ (int64_t) chunk
.length
- q
->index
);
398 q
->index
+= (int64_t) d
;
400 q
->chunk
.length
-= d
;
407 pa_assert(bq
->write_index
>= q
->index
+ (int64_t)q
->chunk
.length
);
408 pa_assert(!q
->next
|| (bq
->write_index
+ (int64_t)chunk
.length
<= q
->next
->index
));
410 /* Try to merge memory blocks */
412 if (q
->chunk
.memblock
== chunk
.memblock
&&
413 q
->chunk
.index
+ q
->chunk
.length
== chunk
.index
&&
414 bq
->write_index
== q
->index
+ (int64_t) q
->chunk
.length
) {
416 q
->chunk
.length
+= chunk
.length
;
417 bq
->write_index
+= (int64_t) chunk
.length
;
421 pa_assert(!bq
->blocks
|| (bq
->write_index
+ (int64_t)chunk
.length
<= bq
->blocks
->index
));
423 if (!(n
= pa_flist_pop(PA_STATIC_FLIST_GET(list_items
))))
424 n
= pa_xnew(struct list_item
, 1);
427 pa_memblock_ref(n
->chunk
.memblock
);
428 n
->index
= bq
->write_index
;
429 bq
->write_index
+= (int64_t) n
->chunk
.length
;
431 n
->next
= q
? q
->next
: bq
->blocks
;
448 write_index_changed(bq
, old
, TRUE
);
452 pa_bool_t
pa_memblockq_prebuf_active(pa_memblockq
*bq
) {
456 return pa_memblockq_get_length(bq
) < bq
->prebuf
;
458 return bq
->prebuf
> 0 && bq
->read_index
>= bq
->write_index
;
461 static pa_bool_t
update_prebuf(pa_memblockq
*bq
) {
466 if (pa_memblockq_get_length(bq
) < bq
->prebuf
)
469 bq
->in_prebuf
= FALSE
;
473 if (bq
->prebuf
> 0 && bq
->read_index
>= bq
->write_index
) {
474 bq
->in_prebuf
= TRUE
;
482 int pa_memblockq_peek(pa_memblockq
* bq
, pa_memchunk
*chunk
) {
487 /* We need to pre-buffer */
488 if (update_prebuf(bq
))
491 fix_current_read(bq
);
493 /* Do we need to spit out silence? */
494 if (!bq
->current_read
|| bq
->current_read
->index
> bq
->read_index
) {
497 /* How much silence shall we return? */
498 if (bq
->current_read
)
499 length
= (size_t) (bq
->current_read
->index
- bq
->read_index
);
500 else if (bq
->write_index
> bq
->read_index
)
501 length
= (size_t) (bq
->write_index
- bq
->read_index
);
505 /* We need to return silence, since no data is yet available */
506 if (bq
->silence
.memblock
) {
507 *chunk
= bq
->silence
;
508 pa_memblock_ref(chunk
->memblock
);
510 if (length
> 0 && length
< chunk
->length
)
511 chunk
->length
= length
;
515 /* If the memblockq is empty, return -1, otherwise return
516 * the time to sleep */
520 chunk
->memblock
= NULL
;
521 chunk
->length
= length
;
528 /* Ok, let's pass real data to the caller */
529 *chunk
= bq
->current_read
->chunk
;
530 pa_memblock_ref(chunk
->memblock
);
532 pa_assert(bq
->read_index
>= bq
->current_read
->index
);
533 d
= bq
->read_index
- bq
->current_read
->index
;
534 chunk
->index
+= (size_t) d
;
535 chunk
->length
-= (size_t) d
;
540 int pa_memblockq_peek_fixed_size(pa_memblockq
*bq
, size_t block_size
, pa_memchunk
*chunk
) {
541 pa_memchunk tchunk
, rchunk
;
543 struct list_item
*item
;
546 pa_assert(block_size
> 0);
548 pa_assert(bq
->silence
.memblock
);
550 if (pa_memblockq_peek(bq
, &tchunk
) < 0)
553 if (tchunk
.length
>= block_size
) {
555 chunk
->length
= block_size
;
559 rchunk
.memblock
= pa_memblock_new(pa_memblock_get_pool(tchunk
.memblock
), block_size
);
561 rchunk
.length
= tchunk
.length
;
563 pa_memchunk_memcpy(&rchunk
, &tchunk
);
564 pa_memblock_unref(tchunk
.memblock
);
566 rchunk
.index
+= tchunk
.length
;
568 /* We don't need to call fix_current_read() here, since
569 * pa_memblock_peek() already did that */
570 item
= bq
->current_read
;
571 ri
= bq
->read_index
+ tchunk
.length
;
573 while (rchunk
.index
< block_size
) {
575 if (!item
|| item
->index
> ri
) {
576 /* Do we need to append silence? */
577 tchunk
= bq
->silence
;
580 tchunk
.length
= PA_MIN(tchunk
.length
, (size_t) (item
->index
- ri
));
585 /* We can append real data! */
586 tchunk
= item
->chunk
;
588 d
= ri
- item
->index
;
589 tchunk
.index
+= (size_t) d
;
590 tchunk
.length
-= (size_t) d
;
592 /* Go to next item for the next iteration */
596 rchunk
.length
= tchunk
.length
= PA_MIN(tchunk
.length
, block_size
- rchunk
.index
);
597 pa_memchunk_memcpy(&rchunk
, &tchunk
);
599 rchunk
.index
+= rchunk
.length
;
604 rchunk
.length
= block_size
;
610 void pa_memblockq_drop(pa_memblockq
*bq
, size_t length
) {
613 pa_assert(length
% bq
->base
== 0);
615 old
= bq
->read_index
;
619 /* Do not drop any data when we are in prebuffering mode */
620 if (update_prebuf(bq
))
623 fix_current_read(bq
);
625 if (bq
->current_read
) {
628 /* We go through this piece by piece to make sure we don't
629 * drop more than allowed by prebuf */
631 p
= bq
->current_read
->index
+ (int64_t) bq
->current_read
->chunk
.length
;
632 pa_assert(p
>= bq
->read_index
);
633 d
= p
- bq
->read_index
;
635 if (d
> (int64_t) length
)
636 d
= (int64_t) length
;
639 length
-= (size_t) d
;
643 /* The list is empty, there's nothing we could drop */
644 bq
->read_index
+= (int64_t) length
;
650 read_index_changed(bq
, old
);
653 void pa_memblockq_rewind(pa_memblockq
*bq
, size_t length
) {
656 pa_assert(length
% bq
->base
== 0);
658 old
= bq
->read_index
;
660 /* This is kind of the inverse of pa_memblockq_drop() */
662 bq
->read_index
-= (int64_t) length
;
664 read_index_changed(bq
, old
);
667 pa_bool_t
pa_memblockq_is_readable(pa_memblockq
*bq
) {
670 if (pa_memblockq_prebuf_active(bq
))
673 if (pa_memblockq_get_length(bq
) <= 0)
679 size_t pa_memblockq_get_length(pa_memblockq
*bq
) {
682 if (bq
->write_index
<= bq
->read_index
)
685 return (size_t) (bq
->write_index
- bq
->read_index
);
688 size_t pa_memblockq_missing(pa_memblockq
*bq
) {
692 if ((l
= pa_memblockq_get_length(bq
)) >= bq
->tlength
)
697 return l
>= bq
->minreq
? l
: 0;
700 void pa_memblockq_seek(pa_memblockq
*bq
, int64_t offset
, pa_seek_mode_t seek
, pa_bool_t account
) {
704 old
= bq
->write_index
;
707 case PA_SEEK_RELATIVE
:
708 bq
->write_index
+= offset
;
710 case PA_SEEK_ABSOLUTE
:
711 bq
->write_index
= offset
;
713 case PA_SEEK_RELATIVE_ON_READ
:
714 bq
->write_index
= bq
->read_index
+ offset
;
716 case PA_SEEK_RELATIVE_END
:
717 bq
->write_index
= (bq
->blocks_tail
? bq
->blocks_tail
->index
+ (int64_t) bq
->blocks_tail
->chunk
.length
: bq
->read_index
) + offset
;
720 pa_assert_not_reached();
724 write_index_changed(bq
, old
, account
);
727 void pa_memblockq_flush_write(pa_memblockq
*bq
, pa_bool_t account
) {
731 pa_memblockq_silence(bq
);
733 old
= bq
->write_index
;
734 bq
->write_index
= bq
->read_index
;
736 pa_memblockq_prebuf_force(bq
);
737 write_index_changed(bq
, old
, account
);
740 void pa_memblockq_flush_read(pa_memblockq
*bq
) {
744 pa_memblockq_silence(bq
);
746 old
= bq
->read_index
;
747 bq
->read_index
= bq
->write_index
;
749 pa_memblockq_prebuf_force(bq
);
750 read_index_changed(bq
, old
);
753 size_t pa_memblockq_get_tlength(pa_memblockq
*bq
) {
759 size_t pa_memblockq_get_minreq(pa_memblockq
*bq
) {
765 size_t pa_memblockq_get_maxrewind(pa_memblockq
*bq
) {
768 return bq
->maxrewind
;
771 int64_t pa_memblockq_get_read_index(pa_memblockq
*bq
) {
774 return bq
->read_index
;
777 int64_t pa_memblockq_get_write_index(pa_memblockq
*bq
) {
780 return bq
->write_index
;
783 int pa_memblockq_push_align(pa_memblockq
* bq
, const pa_memchunk
*chunk
) {
790 return pa_memblockq_push(bq
, chunk
);
792 if (!can_push(bq
, pa_mcalign_csize(bq
->mcalign
, chunk
->length
)))
795 pa_mcalign_push(bq
->mcalign
, chunk
);
797 while (pa_mcalign_pop(bq
->mcalign
, &rchunk
) >= 0) {
799 r
= pa_memblockq_push(bq
, &rchunk
);
800 pa_memblock_unref(rchunk
.memblock
);
803 pa_mcalign_flush(bq
->mcalign
);
811 void pa_memblockq_prebuf_disable(pa_memblockq
*bq
) {
814 bq
->in_prebuf
= FALSE
;
817 void pa_memblockq_prebuf_force(pa_memblockq
*bq
) {
821 bq
->in_prebuf
= TRUE
;
824 size_t pa_memblockq_get_maxlength(pa_memblockq
*bq
) {
827 return bq
->maxlength
;
830 size_t pa_memblockq_get_prebuf(pa_memblockq
*bq
) {
836 size_t pa_memblockq_pop_missing(pa_memblockq
*bq
) {
841 #ifdef MEMBLOCKQ_DEBUG
842 pa_log("[%s] pop: %lli", bq
->name
, (long long) bq
->missing
);
845 if (bq
->missing
<= 0)
848 l
= (size_t) bq
->missing
;
850 bq
->requested
+= bq
->missing
;
853 #ifdef MEMBLOCKQ_DEBUG
854 pa_log("[%s] sent %lli: request counter is at %lli", bq
->name
, (long long) l
, (long long) bq
->requested
);
860 void pa_memblockq_set_maxlength(pa_memblockq
*bq
, size_t maxlength
) {
863 bq
->maxlength
= ((maxlength
+bq
->base
-1)/bq
->base
)*bq
->base
;
865 if (bq
->maxlength
< bq
->base
)
866 bq
->maxlength
= bq
->base
;
868 if (bq
->tlength
> bq
->maxlength
)
869 pa_memblockq_set_tlength(bq
, bq
->maxlength
);
872 void pa_memblockq_set_tlength(pa_memblockq
*bq
, size_t tlength
) {
876 if (tlength
<= 0 || tlength
== (size_t) -1)
877 tlength
= bq
->maxlength
;
879 old_tlength
= bq
->tlength
;
880 bq
->tlength
= ((tlength
+bq
->base
-1)/bq
->base
)*bq
->base
;
882 if (bq
->tlength
> bq
->maxlength
)
883 bq
->tlength
= bq
->maxlength
;
885 if (bq
->minreq
> bq
->tlength
)
886 pa_memblockq_set_minreq(bq
, bq
->tlength
);
888 if (bq
->prebuf
> bq
->tlength
+bq
->base
-bq
->minreq
)
889 pa_memblockq_set_prebuf(bq
, bq
->tlength
+bq
->base
-bq
->minreq
);
891 bq
->missing
+= (int64_t) bq
->tlength
- (int64_t) old_tlength
;
894 void pa_memblockq_set_minreq(pa_memblockq
*bq
, size_t minreq
) {
897 bq
->minreq
= (minreq
/bq
->base
)*bq
->base
;
899 if (bq
->minreq
> bq
->tlength
)
900 bq
->minreq
= bq
->tlength
;
902 if (bq
->minreq
< bq
->base
)
903 bq
->minreq
= bq
->base
;
905 if (bq
->prebuf
> bq
->tlength
+bq
->base
-bq
->minreq
)
906 pa_memblockq_set_prebuf(bq
, bq
->tlength
+bq
->base
-bq
->minreq
);
909 void pa_memblockq_set_prebuf(pa_memblockq
*bq
, size_t prebuf
) {
912 if (prebuf
== (size_t) -1)
913 prebuf
= bq
->tlength
+bq
->base
-bq
->minreq
;
915 bq
->prebuf
= ((prebuf
+bq
->base
-1)/bq
->base
)*bq
->base
;
917 if (prebuf
> 0 && bq
->prebuf
< bq
->base
)
918 bq
->prebuf
= bq
->base
;
920 if (bq
->prebuf
> bq
->tlength
+bq
->base
-bq
->minreq
)
921 bq
->prebuf
= bq
->tlength
+bq
->base
-bq
->minreq
;
923 if (bq
->prebuf
<= 0 || pa_memblockq_get_length(bq
) >= bq
->prebuf
)
924 bq
->in_prebuf
= FALSE
;
927 void pa_memblockq_set_maxrewind(pa_memblockq
*bq
, size_t maxrewind
) {
930 bq
->maxrewind
= (maxrewind
/bq
->base
)*bq
->base
;
933 void pa_memblockq_apply_attr(pa_memblockq
*bq
, const pa_buffer_attr
*a
) {
937 pa_memblockq_set_maxlength(bq
, a
->maxlength
);
938 pa_memblockq_set_tlength(bq
, a
->tlength
);
939 pa_memblockq_set_prebuf(bq
, a
->prebuf
);
940 pa_memblockq_set_minreq(bq
, a
->minreq
);
943 void pa_memblockq_get_attr(pa_memblockq
*bq
, pa_buffer_attr
*a
) {
947 a
->maxlength
= (uint32_t) pa_memblockq_get_maxlength(bq
);
948 a
->tlength
= (uint32_t) pa_memblockq_get_tlength(bq
);
949 a
->prebuf
= (uint32_t) pa_memblockq_get_prebuf(bq
);
950 a
->minreq
= (uint32_t) pa_memblockq_get_minreq(bq
);
953 int pa_memblockq_splice(pa_memblockq
*bq
, pa_memblockq
*source
) {
958 pa_memblockq_prebuf_disable(bq
);
963 if (pa_memblockq_peek(source
, &chunk
) < 0)
966 pa_assert(chunk
.length
> 0);
968 if (chunk
.memblock
) {
970 if (pa_memblockq_push_align(bq
, &chunk
) < 0) {
971 pa_memblock_unref(chunk
.memblock
);
975 pa_memblock_unref(chunk
.memblock
);
977 pa_memblockq_seek(bq
, (int64_t) chunk
.length
, PA_SEEK_RELATIVE
, TRUE
);
979 pa_memblockq_drop(bq
, chunk
.length
);
983 void pa_memblockq_willneed(pa_memblockq
*bq
) {
988 fix_current_read(bq
);
990 for (q
= bq
->current_read
; q
; q
= q
->next
)
991 pa_memchunk_will_need(&q
->chunk
);
994 void pa_memblockq_set_silence(pa_memblockq
*bq
, pa_memchunk
*silence
) {
997 if (bq
->silence
.memblock
)
998 pa_memblock_unref(bq
->silence
.memblock
);
1001 bq
->silence
= *silence
;
1002 pa_memblock_ref(bq
->silence
.memblock
);
1004 pa_memchunk_reset(&bq
->silence
);
1007 pa_bool_t
pa_memblockq_is_empty(pa_memblockq
*bq
) {
1013 void pa_memblockq_silence(pa_memblockq
*bq
) {
1017 drop_block(bq
, bq
->blocks
);
1019 pa_assert(bq
->n_blocks
== 0);
1022 unsigned pa_memblockq_get_nblocks(pa_memblockq
*bq
) {
1025 return bq
->n_blocks
;
1028 size_t pa_memblockq_get_base(pa_memblockq
*bq
) {