2 This file is part of PulseAudio.
4 Copyright 2007 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
8 published by the Free Software Foundation; either version 2.1 of the
9 License, 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 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 #include <pulse/sample.h>
29 #include <pulse/xmalloc.h>
31 #include <pulsecore/endianmacros.h>
32 #include <pulsecore/memchunk.h>
33 #include <pulsecore/macro.h>
34 #include <pulsecore/flist.h>
35 #include <pulsecore/semaphore.h>
36 #include <pulsecore/g711.h>
41 Envelope subsystem for applying linear interpolated volume
42 envelopes on audio data. If multiple enevelopes shall be applied
43 at the same time, the "minimum" envelope is determined and
46 Envelopes are defined in a statically allocated constant structure
47 pa_envelope_def. It may be activated using pa_envelope_add(). And
48 already active envelope may be replaced with pa_envelope_replace()
49 and removed with pa_envelope_remove().The combined "minimum"
50 envelope can be applied to audio data with pa_envelope_apply().
52 _apply() on one hand and _add()/_replace()/_remove() on the other
53 can be executed in seperate threads, in which case no locking is
57 PA_STATIC_FLIST_DECLARE(items
, 0, pa_xfree
);
59 struct pa_envelope_item
{
60 PA_LLIST_FIELDS(pa_envelope_item
);
61 const pa_envelope_def
*def
;
82 pa_sample_spec sample_spec
;
84 PA_LLIST_HEAD(pa_envelope_item
, items
);
91 unsigned n_points
, n_allocated
, n_current
;
102 pa_bool_t cached_valid
;
107 pa_semaphore
*semaphore
;
110 pa_envelope
*pa_envelope_new(const pa_sample_spec
*ss
) {
114 e
= pa_xnew(pa_envelope
, 1);
116 e
->sample_spec
= *ss
;
117 PA_LLIST_HEAD_INIT(pa_envelope_item
, e
->items
);
121 e
->points
[0].n_points
= e
->points
[1].n_points
= 0;
122 e
->points
[0].n_allocated
= e
->points
[1].n_allocated
= 0;
123 e
->points
[0].n_current
= e
->points
[1].n_current
= 0;
124 e
->points
[0].x
= e
->points
[1].x
= NULL
;
125 e
->points
[0].y
.i
= e
->points
[1].y
.i
= NULL
;
126 e
->points
[0].cached_valid
= e
->points
[1].cached_valid
= FALSE
;
128 pa_atomic_store(&e
->state
, STATE_VALID0
);
131 ss
->format
== PA_SAMPLE_FLOAT32LE
||
132 ss
->format
== PA_SAMPLE_FLOAT32BE
;
134 e
->semaphore
= pa_semaphore_new(0);
139 void pa_envelope_free(pa_envelope
*e
) {
143 pa_envelope_remove(e
, e
->items
);
145 pa_xfree(e
->points
[0].x
);
146 pa_xfree(e
->points
[1].x
);
147 pa_xfree(e
->points
[0].y
.i
);
148 pa_xfree(e
->points
[1].y
.i
);
150 pa_semaphore_free(e
->semaphore
);
155 static int32_t linear_interpolate_int(pa_usec_t x1
, int32_t _y1
, pa_usec_t x2
, int32_t y2
, pa_usec_t x3
) {
156 return (int32_t) ((double) _y1
+ (double) (x3
- x1
) * (double) (y2
- _y1
) / (double) (x2
- x1
));
159 static float linear_interpolate_float(pa_usec_t x1
, float _y1
, pa_usec_t x2
, float y2
, pa_usec_t x3
) {
160 return _y1
+ ((float) x3
- (float) x1
) * (y2
- _y1
) / ((float) x2
- (float) x1
);
163 static int32_t item_get_int(pa_envelope_item
*i
, pa_usec_t x
) {
171 if (x
<= i
->def
->points_x
[0])
172 return linear_interpolate_int(0, i
->start_y
.i
,
173 i
->def
->points_x
[0], i
->def
->points_y
.i
[0], x
);
175 if (x
>= i
->def
->points_x
[i
->def
->n_points
-1])
176 return i
->def
->points_y
.i
[i
->def
->n_points
-1];
179 pa_assert(i
->def
->points_x
[i
->j
-1] <= x
);
180 pa_assert(x
<= i
->def
->points_x
[i
->j
]);
182 return linear_interpolate_int(i
->def
->points_x
[i
->j
-1], i
->def
->points_y
.i
[i
->j
-1],
183 i
->def
->points_x
[i
->j
], i
->def
->points_y
.i
[i
->j
], x
);
186 static float item_get_float(pa_envelope_item
*i
, pa_usec_t x
) {
194 if (x
<= i
->def
->points_x
[0])
195 return linear_interpolate_float(0, i
->start_y
.f
,
196 i
->def
->points_x
[0], i
->def
->points_y
.f
[0], x
);
198 if (x
>= i
->def
->points_x
[i
->def
->n_points
-1])
199 return i
->def
->points_y
.f
[i
->def
->n_points
-1];
202 pa_assert(i
->def
->points_x
[i
->j
-1] <= x
);
203 pa_assert(x
<= i
->def
->points_x
[i
->j
]);
205 return linear_interpolate_float(i
->def
->points_x
[i
->j
-1], i
->def
->points_y
.f
[i
->j
-1],
206 i
->def
->points_x
[i
->j
], i
->def
->points_y
.f
[i
->j
], x
);
209 static void envelope_begin_write(pa_envelope
*e
, int *v
) {
210 enum envelope_state new_state
, old_state
;
219 old_state
= pa_atomic_load(&e
->state
);
224 new_state
= STATE_WRITE0
;
228 new_state
= STATE_WRITE1
;
231 new_state
= STATE_WAIT0
;
235 new_state
= STATE_WAIT1
;
239 pa_assert_not_reached();
241 } while (!pa_atomic_cmpxchg(&e
->state
, old_state
, new_state
));
246 pa_semaphore_wait(e
->semaphore
);
250 static pa_bool_t
envelope_commit_write(pa_envelope
*e
, int v
) {
251 enum envelope_state new_state
, old_state
;
256 old_state
= pa_atomic_load(&e
->state
);
261 new_state
= STATE_VALID1
;
265 new_state
= STATE_VALID0
;
273 pa_assert_not_reached();
275 } while (!pa_atomic_cmpxchg(&e
->state
, old_state
, new_state
));
280 static void envelope_begin_read(pa_envelope
*e
, int *v
) {
281 enum envelope_state new_state
, old_state
;
286 old_state
= pa_atomic_load(&e
->state
);
292 new_state
= STATE_READ0
;
297 new_state
= STATE_READ1
;
300 pa_assert_not_reached();
302 } while (!pa_atomic_cmpxchg(&e
->state
, old_state
, new_state
));
305 static void envelope_commit_read(pa_envelope
*e
, int v
) {
306 enum envelope_state new_state
, old_state
;
313 old_state
= pa_atomic_load(&e
->state
);
318 new_state
= STATE_VALID0
;
322 new_state
= STATE_VALID1
;
326 new_state
= STATE_VALID0
;
331 new_state
= STATE_VALID1
;
335 pa_assert_not_reached();
337 } while (!pa_atomic_cmpxchg(&e
->state
, old_state
, new_state
));
340 pa_semaphore_post(e
->semaphore
);
343 static void envelope_merge(pa_envelope
*e
, int v
) {
345 e
->points
[v
].n_points
= 0;
349 pa_usec_t x
= (pa_usec_t
) -1;
351 for (i
= e
->items
; i
; i
= i
->next
)
355 pa_bool_t min_is_set
;
356 pa_envelope_item
*s
= NULL
;
358 /* Let's find the next spot on the X axis to analyze */
359 for (i
= e
->items
; i
; i
= i
->next
) {
363 if (i
->j
>= i
->def
->n_points
)
366 if ((x
!= (pa_usec_t
) -1) && i
->start_x
+ i
->def
->points_x
[i
->j
] <= x
) {
371 if (!s
|| (i
->start_x
+ i
->def
->points_x
[i
->j
] < s
->start_x
+ s
->def
->points_x
[s
->j
]))
381 if (e
->points
[v
].n_points
>= e
->points
[v
].n_allocated
) {
382 e
->points
[v
].n_allocated
= PA_MAX(e
->points
[v
].n_points
*2, PA_ENVELOPE_POINTS_MAX
);
384 e
->points
[v
].x
= pa_xrealloc(e
->points
[v
].x
, sizeof(size_t) * e
->points
[v
].n_allocated
);
385 e
->points
[v
].y
.i
= pa_xrealloc(e
->points
[v
].y
.i
, sizeof(int32_t) * e
->points
[v
].n_allocated
);
388 x
= s
->start_x
+ s
->def
->points_x
[s
->j
];
389 e
->points
[v
].x
[e
->points
[v
].n_points
] = pa_usec_to_bytes(x
, &e
->sample_spec
);
393 /* Now let's find the lowest value */
397 for (i
= e
->items
; i
; i
= i
->next
) {
398 float f
= item_get_float(i
, x
);
399 if (!min_is_set
|| f
< min_f
) {
405 e
->points
[v
].y
.f
[e
->points
[v
].n_points
] = min_f
;
409 for (i
= e
->items
; i
; i
= i
->next
) {
410 int32_t k
= item_get_int(i
, x
);
411 if (!min_is_set
|| k
< min_k
) {
417 e
->points
[v
].y
.i
[e
->points
[v
].n_points
] = min_k
;
420 pa_assert_se(min_is_set
);
421 e
->points
[v
].n_points
++;
425 e
->points
[v
].n_current
= 0;
426 e
->points
[v
].cached_valid
= FALSE
;
429 pa_envelope_item
*pa_envelope_add(pa_envelope
*e
, const pa_envelope_def
*def
) {
435 pa_assert(def
->n_points
> 0);
437 if (!(i
= pa_flist_pop(PA_STATIC_FLIST_GET(items
))))
438 i
= pa_xnew(pa_envelope_item
, 1);
443 i
->start_y
.f
= def
->points_y
.f
[0];
445 i
->start_y
.i
= def
->points_y
.i
[0];
447 PA_LLIST_PREPEND(pa_envelope_item
, e
->items
, i
);
449 envelope_begin_write(e
, &v
);
453 i
->start_x
= pa_bytes_to_usec(e
->x
, &e
->sample_spec
);
454 envelope_merge(e
, v
);
456 } while (!envelope_commit_write(e
, v
));
461 pa_envelope_item
*pa_envelope_replace(pa_envelope
*e
, pa_envelope_item
*i
, const pa_envelope_def
*def
) {
467 pa_assert(def
->n_points
> 0);
469 envelope_begin_write(e
, &v
);
474 uint64_t saved_start_x
;
475 const pa_envelope_def
*saved_def
;
477 x
= pa_bytes_to_usec(e
->x
, &e
->sample_spec
);
480 saved_f
= i
->start_y
.f
;
481 i
->start_y
.f
= item_get_float(i
, x
);
483 saved_i
= i
->start_y
.i
;
484 i
->start_y
.i
= item_get_int(i
, x
);
487 saved_start_x
= i
->start_x
;
493 envelope_merge(e
, v
);
495 if (envelope_commit_write(e
, v
))
498 i
->start_x
= saved_start_x
;
502 i
->start_y
.f
= saved_f
;
504 i
->start_y
.i
= saved_i
;
510 void pa_envelope_remove(pa_envelope
*e
, pa_envelope_item
*i
) {
516 PA_LLIST_REMOVE(pa_envelope_item
, e
->items
, i
);
518 if (pa_flist_push(PA_STATIC_FLIST_GET(items
), i
) < 0)
521 envelope_begin_write(e
, &v
);
523 envelope_merge(e
, v
);
524 } while (!envelope_commit_write(e
, v
));
527 static int32_t linear_get_int(pa_envelope
*e
, int v
) {
530 /* The repeated division could be replaced by Bresenham, as an
533 if (e
->x
< e
->points
[v
].x
[0])
534 return e
->points
[v
].y
.i
[0];
537 if (e
->points
[v
].n_current
+1 >= e
->points
[v
].n_points
)
538 return e
->points
[v
].y
.i
[e
->points
[v
].n_points
-1];
540 if (e
->x
< e
->points
[v
].x
[e
->points
[v
].n_current
+1])
543 e
->points
[v
].n_current
++;
544 e
->points
[v
].cached_valid
= FALSE
;
547 if (!e
->points
[v
].cached_valid
) {
548 e
->points
[v
].cached_dx
= e
->points
[v
].x
[e
->points
[v
].n_current
+1] - e
->points
[v
].x
[e
->points
[v
].n_current
];
549 e
->points
[v
].cached_dy_i
= e
->points
[v
].y
.i
[e
->points
[v
].n_current
+1] - e
->points
[v
].y
.i
[e
->points
[v
].n_current
];
550 e
->points
[v
].cached_valid
= TRUE
;
553 return e
->points
[v
].y
.i
[e
->points
[v
].n_current
] + ((float)e
->points
[v
].cached_dy_i
* (int32_t) (e
->x
- e
->points
[v
].x
[e
->points
[v
].n_current
])) / (int32_t) e
->points
[v
].cached_dx
;
556 static float linear_get_float(pa_envelope
*e
, int v
) {
559 if (e
->x
< e
->points
[v
].x
[0])
560 return e
->points
[v
].y
.f
[0];
563 if (e
->points
[v
].n_current
+1 >= e
->points
[v
].n_points
)
564 return e
->points
[v
].y
.f
[e
->points
[v
].n_points
-1];
566 if (e
->x
< e
->points
[v
].x
[e
->points
[v
].n_current
+1])
569 e
->points
[v
].n_current
++;
570 e
->points
[v
].cached_valid
= FALSE
;
573 if (!e
->points
[v
].cached_valid
) {
574 e
->points
[v
].cached_dy_dx
=
575 (e
->points
[v
].y
.f
[e
->points
[v
].n_current
+1] - e
->points
[v
].y
.f
[e
->points
[v
].n_current
]) /
576 ((float) e
->points
[v
].x
[e
->points
[v
].n_current
+1] - (float) e
->points
[v
].x
[e
->points
[v
].n_current
]);
577 e
->points
[v
].cached_valid
= TRUE
;
580 return e
->points
[v
].y
.f
[e
->points
[v
].n_current
] + (float) (e
->x
- e
->points
[v
].x
[e
->points
[v
].n_current
]) * e
->points
[v
].cached_dy_dx
;
583 void pa_envelope_apply(pa_envelope
*e
, pa_memchunk
*chunk
) {
589 envelope_begin_read(e
, &v
);
591 if (e
->points
[v
].n_points
> 0) {
595 pa_memchunk_make_writable(chunk
, 0);
596 p
= (uint8_t*) pa_memblock_acquire(chunk
->memblock
) + chunk
->index
;
597 fs
= pa_frame_size(&e
->sample_spec
);
600 pa_log_debug("Envelop position %zu applying factor %d=%f, sample spec is %d, chunk's length is %zu, fs is %zu\n", e
->x
, linear_get_int(e
, v
), ((float) linear_get_int(e
,v
))/0x10000, e
->sample_spec
.format
, n
, fs
);
602 switch (e
->sample_spec
.format
) {
607 int32_t factor
= linear_get_int(e
, v
);
609 s
= (uint8_t*) p
+ n
;
611 for (channel
= 0, d
= p
; d
< s
; d
++) {
615 lo
= factor
& 0xFFFF;
617 t
= (int32_t) *d
- 0x80;
618 t
= ((t
* lo
) >> 16) + (t
* hi
);
619 t
= PA_CLAMP_UNLIKELY(t
, -0x80, 0x7F);
620 *d
= (uint8_t) (t
+ 0x80);
622 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
625 factor
= linear_get_int(e
, v
);
632 case PA_SAMPLE_ULAW
: {
635 int32_t factor
= linear_get_int(e
, v
);
637 s
= (uint8_t*) p
+ n
;
639 for (channel
= 0, d
= p
; d
< s
; d
++) {
643 lo
= factor
& 0xFFFF;
645 t
= (int32_t) st_ulaw2linear16(*d
);
646 t
= ((t
* lo
) >> 16) + (t
* hi
);
647 t
= PA_CLAMP_UNLIKELY(t
, -0x8000, 0x7FFF);
648 *d
= (uint8_t) st_14linear2ulaw((int16_t) t
>> 2);
650 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
653 factor
= linear_get_int(e
, v
);
660 case PA_SAMPLE_ALAW
: {
663 int32_t factor
= linear_get_int(e
, v
);
665 s
= (uint8_t*) p
+ n
;
667 for (channel
= 0, d
= p
; d
< s
; d
++) {
671 lo
= factor
& 0xFFFF;
673 t
= (int32_t) st_alaw2linear16(*d
);
674 t
= ((t
* lo
) >> 16) + (t
* hi
);
675 t
= PA_CLAMP_UNLIKELY(t
, -0x8000, 0x7FFF);
676 *d
= (uint8_t) st_13linear2alaw((int16_t) t
>> 3);
678 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
681 factor
= linear_get_int(e
, v
);
688 case PA_SAMPLE_S16NE
: {
691 int32_t factor
= linear_get_int(e
, v
);
693 s
= (int16_t*) p
+ n
/sizeof(int16_t);
695 for (channel
= 0, d
= p
; d
< s
; d
++) {
699 lo
= factor
& 0xFFFF;
702 t
= ((t
* lo
) >> 16) + (t
* hi
);
703 t
= PA_CLAMP_UNLIKELY(t
, -0x8000, 0x7FFF);
706 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
709 factor
= linear_get_int(e
, v
);
716 case PA_SAMPLE_S16RE
: {
719 int32_t factor
= linear_get_int(e
, v
);
721 s
= (int16_t*) p
+ n
/sizeof(int16_t);
723 for (channel
= 0, d
= p
; d
< s
; d
++) {
727 lo
= factor
& 0xFFFF;
729 t
= (int32_t) PA_INT16_SWAP(*d
);
730 t
= ((t
* lo
) >> 16) + (t
* hi
);
731 t
= PA_CLAMP_UNLIKELY(t
, -0x8000, 0x7FFF);
732 *d
= PA_INT16_SWAP((int16_t) t
);
734 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
737 factor
= linear_get_int(e
, v
);
744 case PA_SAMPLE_S32NE
: {
747 int32_t factor
= linear_get_int(e
, v
);
749 s
= (int32_t*) p
+ n
/sizeof(int32_t);
751 for (channel
= 0, d
= p
; d
< s
; d
++) {
755 t
= (t
* factor
) >> 16;
756 t
= PA_CLAMP_UNLIKELY(t
, -0x80000000LL
, 0x7FFFFFFFLL
);
759 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
762 factor
= linear_get_int(e
, v
);
769 case PA_SAMPLE_S32RE
: {
772 int32_t factor
= linear_get_int(e
, v
);
774 s
= (int32_t*) p
+ n
/sizeof(int32_t);
776 for (channel
= 0, d
= p
; d
< s
; d
++) {
779 t
= (int64_t) PA_INT32_SWAP(*d
);
780 t
= (t
* factor
) >> 16;
781 t
= PA_CLAMP_UNLIKELY(t
, -0x80000000LL
, 0x7FFFFFFFLL
);
782 *d
= PA_INT32_SWAP((int32_t) t
);
784 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
787 factor
= linear_get_int(e
, v
);
794 case PA_SAMPLE_FLOAT32NE
: {
795 /*Seems the FLOAT32NE part of pa_volume_memchunk not right, do not reuse here*/
798 for (t
= p
; n
> 0; n
-= fs
) {
799 float factor
= linear_get_float(e
, v
);
803 for (c
= 0; c
< e
->sample_spec
.channels
; c
++, t
++)
810 case PA_SAMPLE_FLOAT32RE
: {
811 /*Seems the FLOAT32RE part of pa_volume_memchunk not right, do not reuse here*/
814 for (t
= p
; n
> 0; n
-= fs
) {
815 float factor
= linear_get_float(e
, v
);
819 for (c
= 0; c
< e
->sample_spec
.channels
; c
++, t
++) {
820 float r
= PA_FLOAT32_SWAP(*t
) * factor
;
821 *t
= PA_FLOAT32_SWAP(r
);
828 case PA_SAMPLE_S24NE
: {
831 int32_t factor
= linear_get_int(e
, v
);
833 s
= (uint8_t*) p
+ n
/3;
835 for (channel
= 0, d
= p
; d
< s
; d
++) {
838 t
= (int64_t)((int32_t) (PA_READ24NE(d
) << 8));
839 t
= (t
* factor
) >> 16;
840 t
= PA_CLAMP_UNLIKELY(t
, -0x80000000LL
, 0x7FFFFFFFLL
);
841 PA_WRITE24NE(d
, ((uint32_t) (int32_t) t
) >> 8);
843 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
846 factor
= linear_get_int(e
, v
);
852 case PA_SAMPLE_S24RE
: {
855 int32_t factor
= linear_get_int(e
, v
);
857 s
= (uint8_t*) p
+ n
/3;
859 for (channel
= 0, d
= p
; d
< s
; d
++) {
862 t
= (int64_t)((int32_t) (PA_READ24RE(d
) << 8));
863 t
= (t
* factor
) >> 16;
864 t
= PA_CLAMP_UNLIKELY(t
, -0x80000000LL
, 0x7FFFFFFFLL
);
865 PA_WRITE24RE(d
, ((uint32_t) (int32_t) t
) >> 8);
867 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
870 factor
= linear_get_int(e
, v
);
876 case PA_SAMPLE_S24_32NE
: {
879 int32_t factor
= linear_get_int(e
, v
);
881 s
= (uint32_t*) p
+ n
/sizeof(uint32_t);
883 for (channel
= 0, d
= p
; d
< s
; d
++) {
886 t
= (int64_t) ((int32_t) (*d
<< 8));
887 t
= (t
* factor
) >> 16;
888 t
= PA_CLAMP_UNLIKELY(t
, -0x80000000LL
, 0x7FFFFFFFLL
);
889 *d
= ((uint32_t) ((int32_t) t
)) >> 8;
891 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
894 factor
= linear_get_int(e
, v
);
900 case PA_SAMPLE_S24_32RE
: {
903 int32_t factor
= linear_get_int(e
, v
);
905 s
= (uint32_t*) p
+ n
/sizeof(uint32_t);
907 for (channel
= 0, d
= p
; d
< s
; d
++) {
910 t
= (int64_t) ((int32_t) (PA_UINT32_SWAP(*d
) << 8));
911 t
= (t
* factor
) >> 16;
912 t
= PA_CLAMP_UNLIKELY(t
, -0x80000000LL
, 0x7FFFFFFFLL
);
913 *d
= PA_UINT32_SWAP(((uint32_t) ((int32_t) t
)) >> 8);
915 if (PA_UNLIKELY(++channel
>= e
->sample_spec
.channels
)) {
918 factor
= linear_get_int(e
, v
);
924 pa_assert_not_reached();
927 case PA_SAMPLE_INVALID
:
928 pa_assert_not_reached();
931 pa_memblock_release(chunk
->memblock
);
933 /* When we have no envelope to apply we reset our origin */
937 envelope_commit_read(e
, v
);
940 void pa_envelope_rewind(pa_envelope
*e
, size_t n_bytes
) {
945 envelope_begin_read(e
, &v
);
947 if (e
->x
- n_bytes
<= e
->points
[v
].x
[0])
948 e
->x
= e
->points
[v
].x
[0];
952 e
->points
[v
].n_current
= 0;
953 e
->points
[v
].cached_valid
= FALSE
;
955 envelope_commit_read(e
, v
);
958 void pa_envelope_restart(pa_envelope
* e
) {
962 envelope_begin_read(e
, &v
);
963 e
->x
= e
->points
[v
].x
[0];
964 envelope_commit_read(e
, v
);
967 pa_bool_t
pa_envelope_is_finished(pa_envelope
* e
) {
972 envelope_begin_read(e
, &v
);
973 finished
= (e
->x
>= e
->points
[v
].x
[e
->points
[v
].n_points
-1]);
974 envelope_commit_read(e
, v
);
979 int32_t pa_envelope_length(pa_envelope
*e
) {
984 envelope_begin_read(e
, &v
);
985 size
= e
->points
[v
].x
[e
->points
[v
].n_points
-1] - e
->points
[v
].x
[0];
986 envelope_commit_read(e
, v
);