2 This file is part of PulseAudio.
4 This module is based off Lennart Poettering's LADSPA sink and swaps out
5 LADSPA functionality for a dbus-aware STFT OLA based digital equalizer.
6 All new work is published under Pulseaudio's original license.
7 Copyright 2009 Jason Newton <nevion@gmail.com>
10 Copyright 2004-2008 Lennart Poettering
12 PulseAudio is free software; you can redistribute it and/or modify
13 it under the terms of the GNU Lesser General Public License as published
14 by the Free Software Foundation; either version 2.1 of the License,
15 or (at your option) any later version.
17 PulseAudio is distributed in the hope that it will be useful, but
18 WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 General Public License for more details.
22 You should have received a copy of the GNU Lesser General Public License
23 along with PulseAudio; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
39 #include <pulse/xmalloc.h>
40 #include <pulse/i18n.h>
42 #include <pulsecore/core-error.h>
43 #include <pulsecore/namereg.h>
44 #include <pulsecore/sink.h>
45 #include <pulsecore/module.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/modargs.h>
48 #include <pulsecore/log.h>
49 #include <pulsecore/thread.h>
50 #include <pulsecore/thread-mq.h>
51 #include <pulsecore/rtpoll.h>
52 #include <pulsecore/sample-util.h>
53 #include <pulsecore/shared.h>
54 #include <pulsecore/idxset.h>
55 #include <pulsecore/strlist.h>
56 #include <pulsecore/database.h>
57 #include <pulsecore/protocol-dbus.h>
58 #include <pulsecore/dbus-util.h>
66 #include <xmmintrin.h>
67 #include <emmintrin.h>
72 #include "module-equalizer-sink-symdef.h"
74 PA_MODULE_AUTHOR("Jason Newton");
75 PA_MODULE_DESCRIPTION(_("General Purpose Equalizer"));
76 PA_MODULE_VERSION(PACKAGE_VERSION
);
77 PA_MODULE_LOAD_ONCE(FALSE
);
78 PA_MODULE_USAGE(_("sink=<sink to connect to> "));
80 #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
86 pa_sink_input
*sink_input
;
90 size_t fft_size
;//length (res) of fft
93 *effectively chooses R
95 size_t R
;/* the hop size between overlapping windows
96 * the latency of the filter, calculated from window_size
97 * based on constraints of COLA and window function
99 size_t latency
;//Really just R but made into it's own variable
100 //for twiddling with pulseaudio
101 size_t overlap_size
;//window_size-R
102 size_t samples_gathered
;
104 float *H
;//frequency response filter (magnitude based)
105 float *W
;//windowing function (time domain)
106 float *work_buffer
, **input
, **overlap_accum
;
107 fftwf_complex
*output_window
;
108 fftwf_plan forward_plan
, inverse_plan
;
111 float *Hs
[2];//thread updatable copies
113 pa_memchunk conv_buffer
;
114 pa_memblockq
*input_q
;
117 pa_dbus_protocol
*dbus_protocol
;
120 pa_database
*database
;
123 static const char* const valid_modargs
[] = {
134 static uint64_t time_diff(struct timespec
*timeA_p
, struct timespec
*timeB_p
);
135 static void hanning_window(float *W
, size_t window_size
);
136 static void fix_filter(float *H
, size_t fft_size
);
137 static void interpolate(float *signal
, size_t length
, uint32_t *xs
, float *ys
, size_t n_points
);
138 static void array_out(const char *name
, float *a
, size_t length
);
139 static int is_monotonic(uint32_t *xs
, size_t length
);
140 static void reset_filter(struct userdata
*u
);
141 static void process_samples(struct userdata
*u
, pa_memchunk
*tchunk
);
142 static void initialize_buffer(struct userdata
*u
, pa_memchunk
*in
);
143 static void input_buffer(struct userdata
*u
, pa_memchunk
*in
);
144 static void save_profile(struct userdata
*u
,char *name
);
145 static void save_state(struct userdata
*u
);
146 static void remove_profile(pa_core
*u
,char *name
);
147 static const char * load_profile(struct userdata
*u
,char *name
);
148 static void load_state(struct userdata
*u
);
151 float * __restrict__ dst
,
152 float * __restrict__ src
,
153 float * __restrict__ overlap
,
154 const float * __restrict__ H
,
155 const float * __restrict__ W
,
156 fftwf_complex
* __restrict__ output_window
,
161 * DBus Routines and Callbacks
163 #define EXTNAME "org.PulseAudio.Ext.Equalizing1"
164 #define MANAGER_PATH "/org/pulseaudio/equalizing1"
165 #define MANAGER_IFACE EXTNAME ".Manager"
166 #define EQUALIZER_IFACE EXTNAME ".Equalizer"
167 static void dbus_init(struct userdata
*u
);
168 static void dbus_done(struct userdata
*u
);
169 static void manager_get_revision(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
170 static void get_sinks(pa_core
*u
, char ***names
, unsigned *n_sinks
);
171 static void manager_get_sinks(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
172 static void get_profiles(pa_core
*u
, char ***names
, unsigned *n_sinks
);
173 static void manager_get_profiles(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
174 static void manager_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
175 static void manager_handle_remove_profile(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
176 static void equalizer_get_revision(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
177 static void equalizer_get_sample_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
178 static void equalizer_get_filter_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
179 static void equalizer_get_n_coefs(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
180 static void equalizer_get_filter(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
181 static void equalizer_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
182 static void equalizer_set_filter(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
183 static void equalizer_handle_seed_filter(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
184 static void equalizer_handle_get_filter_points(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
185 static void equalizer_handle_save_profile(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
186 static void equalizer_handle_load_profile(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
);
187 static void get_filter(struct userdata
*u
, double **H_
);
188 static void set_filter(struct userdata
*u
, double **H_
);
191 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x)
192 #define tdiff(x, y) time_diff(&x, &y)
193 #define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y)
194 #define SINKLIST "equalized_sinklist"
195 #define EQDB "equalizer_db"
197 uint64_t time_diff(struct timespec
*timeA_p
, struct timespec
*timeB_p
)
199 return ((timeA_p
->tv_sec
* 1000000000ULL) + timeA_p
->tv_nsec
) -
200 ((timeB_p
->tv_sec
* 1000000000ULL) + timeB_p
->tv_nsec
);
203 void hanning_window(float *W
, size_t window_size
){
204 //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2
205 for(size_t i
=0; i
< window_size
;++i
){
206 W
[i
] = (float).5*(1-cos(2*M_PI
*i
/(window_size
+1)));
210 void fix_filter(float *H
, size_t fft_size
){
211 //divide out the fft gain
212 for(size_t i
= 0; i
< fft_size
/ 2 + 1; ++i
){
217 void interpolate(float *signal
, size_t length
, uint32_t *xs
, float *ys
, size_t n_points
){
218 //Note that xs must be monotonically increasing!
219 pa_assert_se(n_points
>=2);
220 pa_assert_se(xs
[0] == 0);
221 pa_assert_se(xs
[n_points
- 1] == length
- 1);
222 for(size_t x
= 0, x_range_lower_i
= 0; x
< length
-1; ++x
){
223 pa_assert(x_range_lower_i
< n_points
-1);
224 float x_range_lower
= (float) (xs
[x_range_lower_i
]);
225 float x_range_upper
= (float) (xs
[x_range_lower_i
+1]);
226 pa_assert_se(x_range_lower
< x_range_upper
);
227 pa_assert_se(x
>= x_range_lower
);
228 pa_assert_se(x
<= x_range_upper
);
229 //bilinear-interpolation of coefficients specified
230 float c0
= (x
-x_range_lower
)/(x_range_upper
-x_range_lower
);
231 pa_assert_se(c0
>= 0&&c0
<= 1.0);
232 signal
[x
] = ((1.0f
- c0
) * ys
[x_range_lower_i
] + c0
* ys
[x_range_lower_i
+ 1]);
233 while(x
>= xs
[x_range_lower_i
+ 1]){
237 signal
[length
-1]=ys
[n_points
-1];
240 void array_out(const char *name
, float *a
, size_t length
){
241 FILE *p
=fopen(name
, "w");
243 pa_log("opening %s failed!", name
);
246 for(size_t i
= 0; i
< length
; ++i
){
247 fprintf(p
, "%e,", a
[i
]);
255 static int is_monotonic(uint32_t *xs
,size_t length
){
259 for(size_t i
= 1; i
< length
; ++i
){
268 /* Called from I/O thread context */
269 static int sink_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
270 struct userdata
*u
= PA_SINK(o
)->userdata
;
274 case PA_SINK_MESSAGE_GET_LATENCY
: {
275 size_t fs
=pa_frame_size(&u
->sink
->sample_spec
);
277 /* The sink is _put() before the sink input is, so let's
278 * make sure we don't access it in that time. Also, the
279 * sink input is first shut down, the sink second. */
280 if (!PA_SINK_IS_LINKED(u
->sink
->thread_info
.state
) ||
281 !PA_SINK_INPUT_IS_LINKED(u
->sink_input
->thread_info
.state
)) {
282 *((pa_usec_t
*) data
) = 0;
286 *((pa_usec_t
*) data
) =
287 /* Get the latency of the master sink */
288 pa_sink_get_latency_within_thread(u
->sink_input
->sink
) +
290 /* Add the latency internal to our sink input on top */
291 pa_bytes_to_usec(pa_memblockq_get_length(u
->sink_input
->thread_info
.render_memblockq
), &u
->sink_input
->sink
->sample_spec
) +
292 pa_bytes_to_usec(u
->samples_gathered
* fs
, &u
->sink
->sample_spec
);
293 //+ pa_bytes_to_usec(u->latency * fs, ss)
294 //+ pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss);
299 return pa_sink_process_msg(o
, code
, data
, offset
, chunk
);
303 /* Called from main context */
304 static int sink_set_state(pa_sink
*s
, pa_sink_state_t state
) {
307 pa_sink_assert_ref(s
);
308 pa_assert_se(u
= s
->userdata
);
310 if (!PA_SINK_IS_LINKED(state
) ||
311 !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u
->sink_input
)))
314 pa_sink_input_cork(u
->sink_input
, state
== PA_SINK_SUSPENDED
);
318 /* Called from I/O thread context */
319 static void sink_request_rewind(pa_sink
*s
) {
322 pa_sink_assert_ref(s
);
323 pa_assert_se(u
= s
->userdata
);
325 if (!PA_SINK_IS_LINKED(u
->sink
->thread_info
.state
) ||
326 !PA_SINK_INPUT_IS_LINKED(u
->sink_input
->thread_info
.state
))
329 /* Just hand this one over to the master sink */
330 pa_sink_input_request_rewind(u
->sink_input
, s
->thread_info
.rewind_nbytes
+pa_memblockq_get_length(u
->input_q
), TRUE
, FALSE
, FALSE
);
333 /* Called from I/O thread context */
334 static void sink_update_requested_latency(pa_sink
*s
) {
337 pa_sink_assert_ref(s
);
338 pa_assert_se(u
= s
->userdata
);
340 if (!PA_SINK_IS_LINKED(u
->sink
->thread_info
.state
) ||
341 !PA_SINK_INPUT_IS_LINKED(u
->sink_input
->thread_info
.state
))
344 /* Just hand this one over to the master sink */
345 pa_sink_input_set_requested_latency_within_thread(
347 pa_sink_get_requested_latency_within_thread(s
));
350 static void process_samples(struct userdata
*u
, pa_memchunk
*tchunk
){
351 size_t fs
=pa_frame_size(&(u
->sink
->sample_spec
));
352 pa_assert(u
->samples_gathered
>= u
->R
);
355 tchunk
->length
= u
->R
* fs
;
356 tchunk
->memblock
= pa_memblock_new(u
->sink
->core
->mempool
, tchunk
->length
);
357 dst
= ((float*)pa_memblock_acquire(tchunk
->memblock
));
358 for(size_t c
=0;c
< u
->channels
; c
++) {
368 if(u
->first_iteration
){
369 /* The windowing function will make the audio ramped in, as a cheap fix we can
370 * undo the windowing (for non-zero window values)
372 for(size_t i
= 0;i
< u
->overlap_size
; ++i
){
373 u
->work_buffer
[i
] = u
->W
[i
] <= FLT_EPSILON
? u
->work_buffer
[i
] : u
->work_buffer
[i
] / u
->W
[i
];
376 pa_sample_clamp(PA_SAMPLE_FLOAT32NE
, dst
+ c
, fs
, u
->work_buffer
, sizeof(float), u
->R
);
378 pa_memblock_release(tchunk
->memblock
);
379 u
->samples_gathered
-= u
->R
;
382 typedef float v4sf
__attribute__ ((__aligned__(v_size
* sizeof(float))));
383 typedef union float_vector
{
391 //reference implementation
393 float * __restrict__ dst
,//used as a temp array too, needs to be fft_length!
394 float * __restrict__ src
,/*input data w/ overlap at start,
395 *automatically cycled in routine
397 float * __restrict__ overlap
,//The size of the overlap
398 const float * __restrict__ H
,//The freq. magnitude scalers filter
399 const float * __restrict__ W
,//The windowing function
400 fftwf_complex
* __restrict__ output_window
,//The transformed window'd src
402 //use a linear-phase sliding STFT and overlap-add method (for each channel)
404 memset(dst
+ u
->window_size
, 0, (u
->fft_size
- u
->window_size
) * sizeof(float));
406 for(size_t j
= 0;j
< u
->window_size
; ++j
){
407 dst
[j
] = W
[j
] * src
[j
];
409 //Processing is done here!
411 fftwf_execute_dft_r2c(u
->forward_plan
, dst
, output_window
);
413 for(size_t j
= 0; j
< u
->fft_size
/ 2 + 1; ++j
){
414 u
->output_window
[j
][0] *= u
->H
[j
];
415 u
->output_window
[j
][1] *= u
->H
[j
];
418 fftwf_execute_dft_c2r(u
->inverse_plan
, output_window
, dst
);
419 ////debug: tests overlaping add
420 ////and negates ALL PREVIOUS processing
421 ////yields a perfect reconstruction if COLA is held
422 //for(size_t j = 0; j < u->window_size; ++j){
423 // u->work_buffer[j] = u->W[j] * u->input[c][j];
426 //overlap add and preserve overlap component from this window (linear phase)
427 for(size_t j
= 0;j
< u
->overlap_size
; ++j
){
428 u
->work_buffer
[j
] += overlap
[j
];
429 overlap
[j
] = dst
[u
->R
+j
];
431 ////debug: tests if basic buffering works
432 ////shouldn't modify the signal AT ALL (beyond roundoff)
433 //for(size_t j = 0; j < u->window_size;++j){
434 // u->work_buffer[j] = u->input[c][j];
437 //preseve the needed input for the next window's overlap
438 memmove(src
, src
+u
->R
,
439 ((u
->overlap_size
+ u
->samples_gathered
) - u
->R
)*sizeof(float)
443 ////regardless of sse enabled, the loops in here assume
444 ////16 byte aligned addresses and memory allocations divisible by v_size
446 // float * __restrict__ dst,//used as a temp array too, needs to be fft_length!
447 // float * __restrict__ src,/*input data w/ overlap at start,
448 // *automatically cycled in routine
450 // float * __restrict__ overlap,//The size of the overlap
451 // const float * __restrict__ H,//The freq. magnitude scalers filter
452 // const float * __restrict__ W,//The windowing function
453 // fftwf_complex * __restrict__ output_window,//The transformed window'd src
454 // struct userdata *u){//Collection of constants
456 // const size_t window_size = mround(u->window_size,v_size);
457 // const size_t fft_h = mround(u->fft_size / 2 + 1, v_size / 2);
458 // //const size_t R = mround(u->R, v_size);
459 // const size_t overlap_size = mround(u->overlap_size, v_size);
461 // //assert(u->samples_gathered >= u->R);
462 // //zero out the bit beyond the real overlap so we don't add garbage
463 // for(size_t j = overlap_size; j > u->overlap_size; --j){
466 // //use a linear-phase sliding STFT and overlap-add method
467 // //zero padd the data
468 // memset(dst + u->window_size, 0, (u->fft_size - u->window_size)*sizeof(float));
470 // for(size_t j = 0; j < window_size; j += v_size){
471 // //dst[j] = W[j]*src[j];
472 // float_vector_t *d = (float_vector_t*) (dst+j);
473 // float_vector_t *w = (float_vector_t*) (W+j);
474 // float_vector_t *s = (float_vector_t*) (src+j);
476 // d->m = _mm_mul_ps(w->m, s->m);
478 // d->v = w->v * s->v;
481 // //Processing is done here!
483 // fftwf_execute_dft_r2c(u->forward_plan, dst, output_window);
486 // //perform filtering - purely magnitude based
487 // for(size_t j = 0;j < fft_h; j+=v_size/2){
488 // //output_window[j][0]*=H[j];
489 // //output_window[j][1]*=H[j];
490 // float_vector_t *d = (float_vector_t*)(output_window+j);
492 // h.f[0] = h.f[1] = H[j];
493 // h.f[2] = h.f[3] = H[j+1];
495 // d->m = _mm_mul_ps(d->m, h.m);
501 // fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst);
503 // ////debug: tests overlaping add
504 // ////and negates ALL PREVIOUS processing
505 // ////yields a perfect reconstruction if COLA is held
506 // //for(size_t j = 0; j < u->window_size; ++j){
507 // // dst[j] = W[j]*src[j];
510 // //overlap add and preserve overlap component from this window (linear phase)
511 // for(size_t j = 0; j < overlap_size; j+=v_size){
512 // //dst[j]+=overlap[j];
513 // //overlap[j]+=dst[j+R];
514 // float_vector_t *d = (float_vector_t*)(dst+j);
515 // float_vector_t *o = (float_vector_t*)(overlap+j);
517 // d->m = _mm_add_ps(d->m, o->m);
518 // o->m = ((float_vector_t*)(dst+u->R+j))->m;
521 // o->v = ((float_vector_t*)(dst+u->R+j))->v;
524 // //memcpy(overlap, dst+u->R, u->overlap_size*sizeof(float));
526 // //////debug: tests if basic buffering works
527 // //////shouldn't modify the signal AT ALL (beyond roundoff)
528 // //for(size_t j = 0; j < u->window_size; ++j){
529 // // dst[j] = src[j];
532 // //preseve the needed input for the next window's overlap
533 // memmove(src, src+u->R,
534 // ((u->overlap_size+u->samples_gathered)+-u->R)*sizeof(float)
538 void initialize_buffer(struct userdata
*u
, pa_memchunk
*in
){
539 size_t fs
= pa_frame_size(&(u
->sink
->sample_spec
));
540 size_t samples
= in
->length
/fs
;
541 pa_assert_se(u
->samples_gathered
+ samples
<= u
->window_size
);
542 float *src
= (float*) ((uint8_t*) pa_memblock_acquire(in
->memblock
) + in
->index
);
543 for(size_t c
= 0; c
< u
->channels
; c
++) {
544 //buffer with an offset after the overlap from previous
546 pa_sample_clamp(PA_SAMPLE_FLOAT32NE
, u
->input
[c
]+u
->samples_gathered
, sizeof(float), src
+ c
, fs
, samples
);
548 u
->samples_gathered
+=samples
;
549 pa_memblock_release(in
->memblock
);
552 void input_buffer(struct userdata
*u
, pa_memchunk
*in
){
553 size_t fs
= pa_frame_size(&(u
->sink
->sample_spec
));
554 size_t samples
= in
->length
/fs
;
555 pa_assert_se(samples
<= u
->window_size
- u
->samples_gathered
);
556 float *src
= (float*) ((uint8_t*) pa_memblock_acquire(in
->memblock
) + in
->index
);
557 for(size_t c
= 0; c
< u
->channels
; c
++) {
558 //buffer with an offset after the overlap from previous
561 u
->input
[c
]+u
->samples_gathered
+samples
<= u
->input
[c
]+u
->window_size
563 pa_sample_clamp(PA_SAMPLE_FLOAT32NE
, u
->input
[c
]+u
->overlap_size
+u
->samples_gathered
, sizeof(float), src
+ c
, fs
, samples
);
565 u
->samples_gathered
+= samples
;
566 pa_memblock_release(in
->memblock
);
569 /* Called from I/O thread context */
570 static int sink_input_pop_cb(pa_sink_input
*i
, size_t nbytes
, pa_memchunk
*chunk
) {
572 pa_sink_input_assert_ref(i
);
573 pa_assert_se(u
= i
->userdata
);
576 size_t fs
= pa_frame_size(&(u
->sink
->sample_spec
));
578 chunk
->memblock
= NULL
;
580 /* Hmm, process any rewind request that might be queued up */
581 pa_sink_process_rewind(u
->sink
, 0);
583 //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
584 struct timespec start
, end
;
587 size_t input_remaining
= u
->window_size
- u
->samples_gathered
;
588 pa_assert(input_remaining
> 0);
591 //buffer = &u->conv_buffer;
592 //buffer->length = input_remaining*fs;
594 //pa_memblock_ref(buffer->memblock);
595 //pa_sink_render_into(u->sink, buffer);
596 while(pa_memblockq_peek(u
->input_q
, &tchunk
) < 0 || tchunk
.memblock
== NULL
){
597 pa_sink_render(u
->sink
, input_remaining
*fs
, &tchunk
);
598 pa_assert(tchunk
.memblock
);
599 pa_memblockq_push(u
->input_q
, &tchunk
);
600 pa_memblock_unref(tchunk
.memblock
);
602 pa_assert(tchunk
.memblock
);
603 tchunk
.length
= PA_MIN(input_remaining
* fs
, tchunk
.length
);
604 pa_memblockq_drop(u
->input_q
, tchunk
.length
);
605 //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
608 if(u
->first_iteration
){
609 initialize_buffer(u
, &tchunk
);
611 input_buffer(u
, &tchunk
);
614 //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9);
615 pa_memblock_unref(tchunk
.memblock
);
616 }while(u
->samples_gathered
< u
->window_size
);
618 pa_log_debug("Took %0.6f seconds to get data", tdiff(end
, start
)*1e-9);
620 pa_assert(u
->fft_size
>= u
->window_size
);
621 pa_assert(u
->R
< u
->window_size
);
622 /* set the H filter */
623 unsigned H_i
= pa_aupdate_read_begin(u
->a_H
);
626 /* process a block */
627 process_samples(u
, chunk
);
629 pa_log_debug("Took %0.6f seconds to process", tdiff(end
, start
)*1e-9);
630 pa_aupdate_read_end(u
->a_H
);
632 pa_assert(chunk
->memblock
);
633 //pa_log_debug("gave %ld", chunk->length/fs);
634 //pa_log_debug("end pop");
635 if(u
->first_iteration
){
636 u
->first_iteration
= 0;
641 /* Called from I/O thread context */
642 static void sink_input_process_rewind_cb(pa_sink_input
*i
, size_t nbytes
) {
646 pa_log_debug("Rewind callback!");
647 pa_sink_input_assert_ref(i
);
648 pa_assert_se(u
= i
->userdata
);
650 if (u
->sink
->thread_info
.rewind_nbytes
> 0) {
653 //max_rewrite = nbytes;
654 max_rewrite
= nbytes
+ pa_memblockq_get_length(u
->input_q
);
655 //PA_MIN(pa_memblockq_get_length(u->input_q), nbytes);
656 amount
= PA_MIN(u
->sink
->thread_info
.rewind_nbytes
, max_rewrite
);
657 u
->sink
->thread_info
.rewind_nbytes
= 0;
660 //pa_sample_spec *ss = &u->sink->sample_spec;
661 //invalidate the output q
662 pa_memblockq_seek(u
->input_q
, - (int64_t) amount
, PA_SEEK_RELATIVE
, TRUE
);
663 //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q));
664 //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
665 pa_log("Resetting filter");
670 pa_sink_process_rewind(u
->sink
, amount
);
671 pa_memblockq_rewind(u
->input_q
, nbytes
);
674 void reset_filter(struct userdata
*u
){
675 u
->samples_gathered
= 0;
676 for(size_t i
= 0;i
< u
->channels
; ++i
){
677 memset(u
->overlap_accum
[i
], 0, u
->overlap_size
* sizeof(float));
679 u
->first_iteration
= 1;
682 /* Called from I/O thread context */
683 static void sink_input_update_max_rewind_cb(pa_sink_input
*i
, size_t nbytes
) {
686 pa_sink_input_assert_ref(i
);
687 pa_assert_se(u
= i
->userdata
);
689 pa_memblockq_set_maxrewind(u
->input_q
, nbytes
);
690 pa_sink_set_max_rewind_within_thread(u
->sink
, nbytes
);
693 /* Called from I/O thread context */
694 static void sink_input_update_max_request_cb(pa_sink_input
*i
, size_t nbytes
) {
697 pa_sink_input_assert_ref(i
);
698 pa_assert_se(u
= i
->userdata
);
700 size_t fs
= pa_frame_size(&(u
->sink
->sample_spec
));
701 //pa_sink_set_max_request_within_thread(u->sink, nbytes);
702 //pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
703 pa_sink_set_max_request_within_thread(u
->sink
, ((nbytes
+u
->R
*fs
-1)/(u
->R
*fs
))*(u
->R
*fs
));
706 /* Called from I/O thread context */
707 static void sink_input_update_sink_latency_range_cb(pa_sink_input
*i
) {
710 pa_sink_input_assert_ref(i
);
711 pa_assert_se(u
= i
->userdata
);
713 //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
714 //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs );
715 pa_sink_set_latency_range_within_thread(u
->sink
, i
->sink
->thread_info
.min_latency
, i
->sink
->thread_info
.max_latency
);
718 /* Called from I/O thread context */
719 static void sink_input_update_sink_fixed_latency_cb(pa_sink_input
*i
) {
722 pa_sink_input_assert_ref(i
);
723 pa_assert_se(u
= i
->userdata
);
725 pa_sink_set_fixed_latency_within_thread(u
->sink
, i
->sink
->thread_info
.fixed_latency
);
728 /* Called from I/O thread context */
729 static void sink_input_detach_cb(pa_sink_input
*i
) {
732 pa_sink_input_assert_ref(i
);
733 pa_assert_se(u
= i
->userdata
);
735 pa_sink_detach_within_thread(u
->sink
);
737 pa_sink_set_rtpoll(u
->sink
, NULL
);
740 /* Called from I/O thread context */
741 static void sink_input_attach_cb(pa_sink_input
*i
) {
744 pa_sink_input_assert_ref(i
);
745 pa_assert_se(u
= i
->userdata
);
747 pa_sink_set_rtpoll(u
->sink
, i
->sink
->thread_info
.rtpoll
);
748 pa_sink_set_latency_range_within_thread(u
->sink
, i
->sink
->thread_info
.min_latency
, i
->sink
->thread_info
.max_latency
);
750 pa_sink_set_fixed_latency_within_thread(u
->sink
, i
->sink
->thread_info
.fixed_latency
);
751 pa_sink_attach_within_thread(u
->sink
);
753 //size_t fs = pa_frame_size(&(u->sink->sample_spec));
754 //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
755 //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency);
756 //TODO: setting this guy minimizes drop outs but doesn't get rid
757 //of them completely, figure out why
758 //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
759 //TODO: this guy causes dropouts constantly+rewinds, it's unusable
760 //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
761 pa_sink_attach_within_thread(u
->sink
);
764 /* Called from main context */
765 static void sink_input_kill_cb(pa_sink_input
*i
) {
768 pa_sink_input_assert_ref(i
);
769 pa_assert_se(u
= i
->userdata
);
771 /* The order here matters! We first kill the sink input, followed
772 * by the sink. That means the sink callbacks must be protected
773 * against an unconnected sink input! */
774 pa_sink_input_unlink(u
->sink_input
);
775 pa_sink_unlink(u
->sink
);
777 pa_sink_input_unref(u
->sink_input
);
778 u
->sink_input
= NULL
;
780 pa_sink_unref(u
->sink
);
783 pa_module_unload_request(u
->module
, TRUE
);
786 /* Called from IO thread context */
787 static void sink_input_state_change_cb(pa_sink_input
*i
, pa_sink_input_state_t state
) {
790 pa_sink_input_assert_ref(i
);
791 pa_assert_se(u
= i
->userdata
);
793 /* If we are added for the first time, ask for a rewinding so that
794 * we are heard right-away. */
795 if (PA_SINK_INPUT_IS_LINKED(state
) &&
796 i
->thread_info
.state
== PA_SINK_INPUT_INIT
) {
797 pa_log_debug("Requesting rewind due to state change.");
798 pa_sink_input_request_rewind(i
, 0, FALSE
, TRUE
, TRUE
);
802 void save_profile(struct userdata
*u
, char *name
){
803 float *H_n
= pa_xmalloc((u
->fft_size
/ 2 + 1) * sizeof(float));
804 const float *H
= u
->Hs
[pa_aupdate_read_begin(u
->a_H
)];
805 for(size_t i
= 0 ; i
<= u
->fft_size
/ 2 + 1; ++i
){
806 //H_n[i] = H[i] * u->fft_size;
809 pa_aupdate_read_end(u
->a_H
);
812 key
.size
= strlen(key
.data
);
814 data
.size
= (u
->fft_size
/ 2 + 1) * sizeof(float);
815 pa_database_set(u
->database
, &key
, &data
, TRUE
);
816 pa_database_sync(u
->database
);
819 void save_state(struct userdata
*u
){
820 char *state_name
= pa_sprintf_malloc("%s-previous-state", u
->name
);
821 save_profile(u
, state_name
);
822 pa_xfree(state_name
);
825 void remove_profile(pa_core
*c
,char *name
){
828 key
.size
= strlen(key
.data
);
829 pa_database
*database
;
830 pa_assert_se(database
= pa_shared_get(c
,EQDB
));
831 pa_database_unset(database
,&key
);
832 pa_database_sync(database
);
835 const char* load_profile(struct userdata
*u
,char *name
){
838 key
.size
= strlen(key
.data
);
839 if(pa_database_get(u
->database
, &key
, &value
) != NULL
){
840 if(value
.size
== (u
->fft_size
/ 2 + 1) * sizeof(float)){
841 float *H
=u
->Hs
[pa_aupdate_write_begin(u
->a_H
)];
842 memcpy(H
, value
.data
, value
.size
);
843 pa_aupdate_write_swap(u
->a_H
);
844 pa_aupdate_write_end(u
->a_H
);
846 return "incompatible size";
848 pa_datum_free(&value
);
850 return "profile doesn't exist";
853 //fix_filter(u->H, u->fft_size);
855 void load_state(struct userdata
*u
){
856 char *state_name
=pa_sprintf_malloc("%s-previous-state", u
->name
);
857 load_profile(u
,state_name
);
858 pa_xfree(state_name
);
862 /* Called from main context */
863 static pa_bool_t
sink_input_may_move_to_cb(pa_sink_input
*i
, pa_sink
*dest
) {
866 pa_sink_input_assert_ref(i
);
867 pa_assert_se(u
= i
->userdata
);
869 return u
->sink
!= dest
;
872 /* Called from main context */
873 static void sink_input_moving_cb(pa_sink_input
*i
, pa_sink
*dest
) {
876 pa_sink_input_assert_ref(i
);
877 pa_assert_se(u
= i
->userdata
);
879 pa_sink_set_asyncmsgq(u
->sink
, dest
->asyncmsgq
);
880 pa_sink_update_flags(u
->sink
, PA_SINK_LATENCY
|PA_SINK_DYNAMIC_LATENCY
, dest
->flags
);
883 //ensure's memory allocated is a multiple of v_size
885 static void * alloc(size_t x
,size_t s
){
886 size_t f
= mround(x
*s
, sizeof(float)*v_size
);
887 pa_assert_se(f
>= x
*s
);
888 //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16);
889 //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16);
890 float *t
= fftwf_malloc(f
);
895 int pa__init(pa_module
*m
) {
902 pa_sink_input_new_data sink_input_data
;
903 pa_sink_new_data sink_data
;
904 pa_bool_t
*use_default
= NULL
;
909 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
910 pa_log("Failed to parse module arguments.");
914 if (!(master
= pa_namereg_get(m
->core
, pa_modargs_get_value(ma
, "master", NULL
), PA_NAMEREG_SINK
))) {
915 pa_log("Master sink not found");
919 ss
= master
->sample_spec
;
920 ss
.format
= PA_SAMPLE_FLOAT32
;
921 map
= master
->channel_map
;
922 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_DEFAULT
) < 0) {
923 pa_log("Invalid sample format specification or channel map");
926 fs
= pa_frame_size(&ss
);
928 u
= pa_xnew0(struct userdata
, 1);
932 u
->channels
= ss
.channels
;
933 u
->fft_size
= pow(2, ceil(log(ss
.rate
)/log(2)));
934 pa_log_debug("fft size: %ld", u
->fft_size
);
935 u
->window_size
= 15999;
936 u
->R
= (u
->window_size
+ 1) / 2;
937 u
->overlap_size
= u
->window_size
- u
->R
;
938 u
->samples_gathered
= 0;
939 u
->input_q
= pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH
, 0, fs
, 1, 1, 0, NULL
);
940 u
->a_H
= pa_aupdate_new();
941 u
->latency
= u
->window_size
- u
->R
;
942 for(size_t i
= 0; i
< 2; ++i
){
943 u
->Hs
[i
] = alloc((u
->fft_size
/ 2 + 1), sizeof(float));
945 u
->W
= alloc(u
->window_size
, sizeof(float));
946 u
->work_buffer
= alloc(u
->fft_size
, sizeof(float));
947 memset(u
->work_buffer
, 0, u
->fft_size
*sizeof(float));
948 u
->input
= (float **)pa_xmalloc0(sizeof(float *)*u
->channels
);
949 u
->overlap_accum
= (float **)pa_xmalloc0(sizeof(float *)*u
->channels
);
950 for(size_t c
= 0; c
< u
->channels
; ++c
){
951 u
->input
[c
] = alloc(u
->window_size
, sizeof(float));
952 pa_assert_se(u
->input
[c
]);
953 memset(u
->input
[c
], 0, (u
->window_size
)*sizeof(float));
954 pa_assert_se(u
->input
[c
]);
955 u
->overlap_accum
[c
] = alloc(u
->overlap_size
, sizeof(float));
956 pa_assert_se(u
->overlap_accum
[c
]);
957 memset(u
->overlap_accum
[c
], 0, u
->overlap_size
*sizeof(float));
959 u
->output_window
= alloc((u
->fft_size
/ 2 + 1), sizeof(fftwf_complex
));
960 u
->forward_plan
= fftwf_plan_dft_r2c_1d(u
->fft_size
, u
->work_buffer
, u
->output_window
, FFTW_ESTIMATE
);
961 u
->inverse_plan
= fftwf_plan_dft_c2r_1d(u
->fft_size
, u
->output_window
, u
->work_buffer
, FFTW_ESTIMATE
);
963 hanning_window(u
->W
, u
->window_size
);
964 u
->first_iteration
= 1;
967 pa_sink_new_data_init(&sink_data
);
968 sink_data
.driver
= __FILE__
;
969 sink_data
.module
= m
;
970 if (!(sink_data
.name
= pa_xstrdup(pa_modargs_get_value(ma
, "sink_name", NULL
))))
971 sink_data
.name
= pa_sprintf_malloc("%s.equalizer", master
->name
);
972 pa_sink_new_data_set_sample_spec(&sink_data
, &ss
);
973 pa_sink_new_data_set_channel_map(&sink_data
, &map
);
974 z
= pa_proplist_gets(master
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
975 pa_proplist_setf(sink_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, "FFT based equalizer on %s",z
? z
: master
->name
);
976 pa_proplist_sets(sink_data
.proplist
, PA_PROP_DEVICE_MASTER_DEVICE
, master
->name
);
977 pa_proplist_sets(sink_data
.proplist
, PA_PROP_DEVICE_CLASS
, "filter");
979 if (pa_modargs_get_proplist(ma
, "sink_properties", sink_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
980 pa_log("Invalid properties");
981 pa_sink_new_data_done(&sink_data
);
985 u
->sink
= pa_sink_new(m
->core
, &sink_data
, master
->flags
& (PA_SINK_LATENCY
|PA_SINK_DYNAMIC_LATENCY
));
986 pa_sink_new_data_done(&sink_data
);
989 pa_log("Failed to create sink.");
992 u
->name
=pa_xstrdup(u
->sink
->name
);
993 u
->sink
->parent
.process_msg
= sink_process_msg
;
994 u
->sink
->set_state
= sink_set_state
;
995 u
->sink
->update_requested_latency
= sink_update_requested_latency
;
996 u
->sink
->request_rewind
= sink_request_rewind
;
997 u
->sink
->userdata
= u
;
999 pa_sink_set_asyncmsgq(u
->sink
, master
->asyncmsgq
);
1000 pa_sink_set_max_request(u
->sink
,
1001 ((pa_sink_get_max_request(u
->sink
)+u
->R
*fs
-1)/(u
->R
*fs
))*(u
->R
*fs
)
1003 //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss));
1005 /* Create sink input */
1006 pa_sink_input_new_data_init(&sink_input_data
);
1007 sink_input_data
.driver
= __FILE__
;
1008 sink_input_data
.module
= m
;
1009 sink_input_data
.sink
= master
;
1010 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_NAME
, "Equalized Stream");
1011 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_ROLE
, "filter");
1012 pa_sink_input_new_data_set_sample_spec(&sink_input_data
, &ss
);
1013 pa_sink_input_new_data_set_channel_map(&sink_input_data
, &map
);
1015 pa_sink_input_new(&u
->sink_input
, m
->core
, &sink_input_data
, 0);
1016 pa_sink_input_new_data_done(&sink_input_data
);
1021 u
->sink_input
->pop
= sink_input_pop_cb
;
1022 u
->sink_input
->process_rewind
= sink_input_process_rewind_cb
;
1023 u
->sink_input
->update_max_rewind
= sink_input_update_max_rewind_cb
;
1024 u
->sink_input
->update_max_request
= sink_input_update_max_request_cb
;
1025 u
->sink_input
->update_sink_latency_range
= sink_input_update_sink_latency_range_cb
;
1026 u
->sink_input
->update_sink_fixed_latency
= sink_input_update_sink_fixed_latency_cb
;
1027 u
->sink_input
->kill
= sink_input_kill_cb
;
1028 u
->sink_input
->attach
= sink_input_attach_cb
;
1029 u
->sink_input
->detach
= sink_input_detach_cb
;
1030 u
->sink_input
->state_change
= sink_input_state_change_cb
;
1031 u
->sink_input
->may_move_to
= sink_input_may_move_to_cb
;
1032 u
->sink_input
->moving
= sink_input_moving_cb
;
1033 u
->sink_input
->userdata
= u
;
1035 pa_sink_put(u
->sink
);
1036 pa_sink_input_put(u
->sink_input
);
1038 pa_modargs_free(ma
);
1040 pa_xfree(use_default
);
1044 //default filter to these
1045 float *H
=u
->Hs
[pa_aupdate_write_begin(u
->a_H
)];
1046 for(size_t i
= 0; i
< u
->fft_size
/ 2 + 1; ++i
){
1047 H
[i
] = 1.0 / sqrtf(2.0f
);
1049 fix_filter(H
, u
->fft_size
);
1050 pa_aupdate_write_swap(u
->a_H
);
1051 pa_aupdate_write_end(u
->a_H
);
1052 //load old parameters
1059 pa_modargs_free(ma
);
1061 pa_xfree(use_default
);
1068 int pa__get_n_used(pa_module
*m
) {
1072 pa_assert_se(u
= m
->userdata
);
1074 return pa_sink_linked_by(u
->sink
);
1077 void pa__done(pa_module
*m
) {
1082 if (!(u
= m
->userdata
))
1089 /* See comments in sink_input_kill_cb() above regarding
1090 * destruction order! */
1093 pa_sink_input_unlink(u
->sink_input
);
1096 pa_sink_unlink(u
->sink
);
1099 pa_sink_input_unref(u
->sink_input
);
1102 pa_sink_unref(u
->sink
);
1104 pa_aupdate_free(u
->a_H
);
1105 pa_memblockq_free(u
->input_q
);
1107 fftwf_destroy_plan(u
->inverse_plan
);
1108 fftwf_destroy_plan(u
->forward_plan
);
1109 pa_xfree(u
->output_window
);
1110 for(size_t c
=0; c
< u
->channels
; ++c
){
1111 pa_xfree(u
->overlap_accum
[c
]);
1112 pa_xfree(u
->input
[c
]);
1114 pa_xfree(u
->overlap_accum
);
1116 pa_xfree(u
->work_buffer
);
1118 for(size_t i
= 0; i
< 2; ++i
){
1127 enum manager_method_index
{
1128 MANAGER_METHOD_REMOVE_PROFILE
,
1132 pa_dbus_arg_info remove_profile_args
[]={
1136 static pa_dbus_method_handler manager_methods
[MANAGER_METHOD_MAX
]={
1137 [MANAGER_METHOD_REMOVE_PROFILE
]{
1138 .method_name
="RemoveProfile",
1139 .arguments
=remove_profile_args
,
1140 .n_arguments
=sizeof(remove_profile_args
)/sizeof(pa_dbus_arg_info
),
1141 .receive_cb
=manager_handle_remove_profile
}
1144 enum manager_handler_index
{
1145 MANAGER_HANDLER_REVISION
,
1146 MANAGER_HANDLER_EQUALIZED_SINKS
,
1147 MANAGER_HANDLER_PROFILES
,
1151 static pa_dbus_property_handler manager_handlers
[MANAGER_HANDLER_MAX
]={
1152 [MANAGER_HANDLER_REVISION
]={.property_name
="InterfaceRevision",.type
="u",.get_cb
=manager_get_revision
,.set_cb
=NULL
},
1153 [MANAGER_HANDLER_EQUALIZED_SINKS
]={.property_name
="EqualizedSinks",.type
="ao",.get_cb
=manager_get_sinks
,.set_cb
=NULL
},
1154 [MANAGER_HANDLER_PROFILES
]={.property_name
="Profiles",.type
="as",.get_cb
=manager_get_profiles
,.set_cb
=NULL
}
1157 pa_dbus_arg_info sink_args
[]={
1161 enum manager_signal_index
{
1162 MANAGER_SIGNAL_SINK_ADDED
,
1163 MANAGER_SIGNAL_SINK_REMOVED
,
1164 MANAGER_SIGNAL_PROFILES_CHANGED
,
1168 static pa_dbus_signal_info manager_signals
[MANAGER_SIGNAL_MAX
]={
1169 [MANAGER_SIGNAL_SINK_ADDED
]={.name
="SinkAdded", .arguments
=sink_args
, .n_arguments
=sizeof(sink_args
)/sizeof(pa_dbus_arg_info
)},
1170 [MANAGER_SIGNAL_SINK_REMOVED
]={.name
="SinkRemoved", .arguments
=sink_args
, .n_arguments
=sizeof(sink_args
)/sizeof(pa_dbus_arg_info
)},
1171 [MANAGER_SIGNAL_PROFILES_CHANGED
]={.name
="ProfilesChanged", .arguments
=NULL
, .n_arguments
=0}
1174 static pa_dbus_interface_info manager_info
={
1175 .name
=MANAGER_IFACE
,
1176 .method_handlers
=manager_methods
,
1177 .n_method_handlers
=MANAGER_METHOD_MAX
,
1178 .property_handlers
=manager_handlers
,
1179 .n_property_handlers
=MANAGER_HANDLER_MAX
,
1180 .get_all_properties_cb
=manager_get_all
,
1181 .signals
=manager_signals
,
1182 .n_signals
=MANAGER_SIGNAL_MAX
1185 enum equalizer_method_index
{
1186 EQUALIZER_METHOD_FILTER_POINTS
,
1187 EQUALIZER_METHOD_SEED_FILTER
,
1188 EQUALIZER_METHOD_SAVE_PROFILE
,
1189 EQUALIZER_METHOD_LOAD_PROFILE
,
1190 EQUALIZER_METHOD_MAX
1193 enum equalizer_handler_index
{
1194 EQUALIZER_HANDLER_REVISION
,
1195 EQUALIZER_HANDLER_SAMPLERATE
,
1196 EQUALIZER_HANDLER_FILTERSAMPLERATE
,
1197 EQUALIZER_HANDLER_N_COEFS
,
1198 EQUALIZER_HANDLER_FILTER
,
1199 EQUALIZER_HANDLER_MAX
1202 pa_dbus_arg_info filter_points_args
[]={
1206 pa_dbus_arg_info seed_filter_args
[]={
1210 pa_dbus_arg_info save_profile_args
[]={
1213 pa_dbus_arg_info load_profile_args
[]={
1217 static pa_dbus_method_handler equalizer_methods
[EQUALIZER_METHOD_MAX
]={
1218 [EQUALIZER_METHOD_SEED_FILTER
]{
1219 .method_name
="SeedFilter",
1220 .arguments
=seed_filter_args
,
1221 .n_arguments
=sizeof(seed_filter_args
)/sizeof(pa_dbus_arg_info
),
1222 .receive_cb
=equalizer_handle_seed_filter
},
1223 [EQUALIZER_METHOD_FILTER_POINTS
]{
1224 .method_name
="FilterAtPoints",
1225 .arguments
=filter_points_args
,
1226 .n_arguments
=sizeof(filter_points_args
)/sizeof(pa_dbus_arg_info
),
1227 .receive_cb
=equalizer_handle_get_filter_points
},
1228 [EQUALIZER_METHOD_SAVE_PROFILE
]{
1229 .method_name
="SaveProfile",
1230 .arguments
=save_profile_args
,
1231 .n_arguments
=sizeof(save_profile_args
)/sizeof(pa_dbus_arg_info
),
1232 .receive_cb
=equalizer_handle_save_profile
},
1233 [EQUALIZER_METHOD_LOAD_PROFILE
]{
1234 .method_name
="LoadProfile",
1235 .arguments
=load_profile_args
,
1236 .n_arguments
=sizeof(load_profile_args
)/sizeof(pa_dbus_arg_info
),
1237 .receive_cb
=equalizer_handle_load_profile
},
1240 static pa_dbus_property_handler equalizer_handlers
[EQUALIZER_HANDLER_MAX
]={
1241 [EQUALIZER_HANDLER_REVISION
]={.property_name
="InterfaceRevision",.type
="u",.get_cb
=equalizer_get_revision
,.set_cb
=NULL
},
1242 [EQUALIZER_HANDLER_SAMPLERATE
]{.property_name
="SampleRate",.type
="u",.get_cb
=equalizer_get_sample_rate
,.set_cb
=NULL
},
1243 [EQUALIZER_HANDLER_FILTERSAMPLERATE
]{.property_name
="FilterSampleRate",.type
="u",.get_cb
=equalizer_get_filter_rate
,.set_cb
=NULL
},
1244 [EQUALIZER_HANDLER_N_COEFS
]{.property_name
="NFilterCoefficients",.type
="u",.get_cb
=equalizer_get_n_coefs
,.set_cb
=NULL
},
1245 [EQUALIZER_HANDLER_FILTER
]{.property_name
="Filter",.type
="ad",.get_cb
=equalizer_get_filter
,.set_cb
=equalizer_set_filter
},
1248 enum equalizer_signal_index
{
1249 EQUALIZER_SIGNAL_FILTER_CHANGED
,
1250 EQUALIZER_SIGNAL_SINK_RECONFIGURED
,
1251 EQUALIZER_SIGNAL_MAX
1254 static pa_dbus_signal_info equalizer_signals
[EQUALIZER_SIGNAL_MAX
]={
1255 [EQUALIZER_SIGNAL_FILTER_CHANGED
]={.name
="FilterChanged", .arguments
=NULL
, .n_arguments
=0},
1256 [EQUALIZER_SIGNAL_SINK_RECONFIGURED
]={.name
="SinkReconfigured", .arguments
=NULL
, .n_arguments
=0},
1259 static pa_dbus_interface_info equalizer_info
={
1260 .name
=EQUALIZER_IFACE
,
1261 .method_handlers
=equalizer_methods
,
1262 .n_method_handlers
=EQUALIZER_METHOD_MAX
,
1263 .property_handlers
=equalizer_handlers
,
1264 .n_property_handlers
=EQUALIZER_HANDLER_MAX
,
1265 .get_all_properties_cb
=equalizer_get_all
,
1266 .signals
=equalizer_signals
,
1267 .n_signals
=EQUALIZER_SIGNAL_MAX
1270 void dbus_init(struct userdata
*u
){
1271 u
->dbus_protocol
=pa_dbus_protocol_get(u
->sink
->core
);
1272 u
->dbus_path
=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u
->sink
->index
);
1274 pa_dbus_protocol_add_interface(u
->dbus_protocol
, u
->dbus_path
, &equalizer_info
, u
);
1275 pa_idxset
*sink_list
=pa_shared_get(u
->sink
->core
, SINKLIST
);
1276 u
->database
=pa_shared_get(u
->sink
->core
, EQDB
);
1277 if(sink_list
==NULL
){
1278 sink_list
=pa_idxset_new(&pa_idxset_trivial_hash_func
, &pa_idxset_trivial_compare_func
);
1279 pa_shared_set(u
->sink
->core
, SINKLIST
, sink_list
);
1281 pa_assert_se(dbname
= pa_state_path("equalizers", TRUE
));
1282 pa_assert_se(u
->database
= pa_database_open(dbname
, TRUE
));
1284 pa_shared_set(u
->sink
->core
,EQDB
,u
->database
);
1285 pa_dbus_protocol_add_interface(u
->dbus_protocol
, MANAGER_PATH
, &manager_info
, u
->sink
->core
);
1286 pa_dbus_protocol_register_extension(u
->dbus_protocol
, EXTNAME
);
1289 pa_idxset_put(sink_list
, u
, &dummy
);
1291 DBusMessage
*signal
= NULL
;
1292 pa_assert_se((signal
= dbus_message_new_signal(MANAGER_PATH
, MANAGER_IFACE
, manager_signals
[MANAGER_SIGNAL_SINK_ADDED
].name
)));
1293 dbus_message_append_args(signal
, DBUS_TYPE_OBJECT_PATH
, &u
->dbus_path
, DBUS_TYPE_INVALID
);
1294 pa_dbus_protocol_send_signal(u
->dbus_protocol
, signal
);
1295 dbus_message_unref(signal
);
1298 void dbus_done(struct userdata
*u
){
1299 pa_idxset
*sink_list
;
1302 DBusMessage
*signal
= NULL
;
1303 pa_assert_se((signal
= dbus_message_new_signal(MANAGER_PATH
, MANAGER_IFACE
, manager_signals
[MANAGER_SIGNAL_SINK_REMOVED
].name
)));
1304 dbus_message_append_args(signal
, DBUS_TYPE_OBJECT_PATH
, &u
->dbus_path
, DBUS_TYPE_INVALID
);
1305 pa_dbus_protocol_send_signal(u
->dbus_protocol
, signal
);
1306 dbus_message_unref(signal
);
1308 pa_assert_se(sink_list
=pa_shared_get(u
->sink
->core
,SINKLIST
));
1309 pa_idxset_remove_by_data(sink_list
,u
,&dummy
);
1310 if(pa_idxset_size(sink_list
)==0){
1311 pa_dbus_protocol_unregister_extension(u
->dbus_protocol
, EXTNAME
);
1312 pa_dbus_protocol_remove_interface(u
->dbus_protocol
, MANAGER_PATH
, manager_info
.name
);
1313 pa_shared_remove(u
->sink
->core
, EQDB
);
1314 pa_database_close(u
->database
);
1315 pa_shared_remove(u
->sink
->core
, SINKLIST
);
1316 pa_xfree(sink_list
);
1318 pa_dbus_protocol_remove_interface(u
->dbus_protocol
, u
->dbus_path
, equalizer_info
.name
);
1319 pa_xfree(u
->dbus_path
);
1320 pa_dbus_protocol_unref(u
->dbus_protocol
);
1323 void manager_handle_remove_profile(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
) {
1325 pa_core
*c
= (pa_core
*)_u
;
1329 dbus_error_init(&error
);
1331 if(!dbus_message_get_args(msg
, &error
,
1332 DBUS_TYPE_STRING
, &name
,
1333 DBUS_TYPE_INVALID
)){
1334 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "%s", error
.message
);
1335 dbus_error_free(&error
);
1338 remove_profile(c
,name
);
1339 pa_dbus_send_empty_reply(conn
, msg
);
1341 DBusMessage
*signal
= NULL
;
1342 pa_assert_se((signal
= dbus_message_new_signal(MANAGER_PATH
, MANAGER_IFACE
, manager_signals
[MANAGER_SIGNAL_PROFILES_CHANGED
].name
)));
1343 pa_dbus_protocol
*dbus_protocol
= pa_dbus_protocol_get(c
);
1344 pa_dbus_protocol_send_signal(dbus_protocol
, signal
);
1345 pa_dbus_protocol_unref(dbus_protocol
);
1346 dbus_message_unref(signal
);
1349 void manager_get_revision(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1351 pa_dbus_send_basic_value_reply(conn
, msg
, DBUS_TYPE_UINT32
, &rev
);
1354 void get_sinks(pa_core
*u
, char ***names
, unsigned *n_sinks
){
1360 struct userdata
*sink_u
= NULL
;
1362 pa_idxset
*sink_list
;
1363 pa_assert_se(sink_list
= pa_shared_get(u
, SINKLIST
));
1364 *n_sinks
= (unsigned) pa_idxset_size(sink_list
);
1365 *names
= *n_sinks
> 0 ? pa_xnew0(char *,*n_sinks
) : NULL
;
1366 for(uint32_t i
= 0; i
< *n_sinks
; ++i
){
1367 sink_u
= (struct userdata
*) pa_idxset_iterate(sink_list
, &iter
, &dummy
);
1368 (*names
)[i
] = pa_xstrdup(sink_u
->dbus_path
);
1372 void manager_get_sinks(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1378 char **names
= NULL
;
1379 get_sinks((pa_core
*) _u
, &names
, &n
);
1380 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_OBJECT_PATH
, names
, n
);
1381 for(unsigned i
= 0; i
< n
; ++i
){
1387 void get_profiles(pa_core
*c
, char ***names
, unsigned *n
){
1392 pa_database
*database
;
1393 pa_assert_se(database
= pa_shared_get(c
, EQDB
));
1394 pa_datum key
, next_key
;
1395 pa_strlist
*head
=NULL
, *iter
;
1398 done
= !pa_database_first(database
, &key
, NULL
);
1401 done
= !pa_database_next(database
, &key
, &next_key
, NULL
);
1402 name
=pa_xmalloc(key
.size
+ 1);
1403 memcpy(name
, key
.data
, key
.size
);
1404 name
[key
.size
] = '\0';
1405 pa_datum_free(&key
);
1406 head
= pa_strlist_prepend(head
, name
);
1411 (*names
) = *n
> 0 ? pa_xnew0(char *, *n
) : NULL
;
1413 for(unsigned i
= 0; i
< *n
; ++i
){
1414 (*names
)[*n
-1-i
]=pa_xstrdup(pa_strlist_data(iter
));
1415 iter
=pa_strlist_next(iter
);
1417 pa_strlist_free(head
);
1420 void manager_get_profiles(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1427 get_profiles((pa_core
*)_u
, &names
, &n
);
1428 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_STRING
, names
, n
);
1429 for(unsigned i
= 0; i
< n
; ++i
){
1435 void manager_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1440 pa_core
*c
= (pa_core
*)_u
;
1441 char **names
= NULL
;
1443 DBusMessage
*reply
= NULL
;
1444 DBusMessageIter msg_iter
, dict_iter
;
1445 pa_assert_se((reply
= dbus_message_new_method_return(msg
)));
1446 dbus_message_iter_init_append(reply
, &msg_iter
);
1447 pa_assert_se(dbus_message_iter_open_container(&msg_iter
, DBUS_TYPE_ARRAY
, "{sv}", &dict_iter
));
1450 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, manager_handlers
[MANAGER_HANDLER_REVISION
].property_name
, DBUS_TYPE_UINT32
, &rev
);
1452 get_sinks(c
, &names
, &n
);
1453 pa_dbus_append_basic_array_variant_dict_entry(&dict_iter
,manager_handlers
[MANAGER_HANDLER_EQUALIZED_SINKS
].property_name
, DBUS_TYPE_OBJECT_PATH
, names
, n
);
1454 for(unsigned i
= 0; i
< n
; ++i
){
1459 get_profiles(c
, &names
, &n
);
1460 pa_dbus_append_basic_array_variant_dict_entry(&dict_iter
, manager_handlers
[MANAGER_HANDLER_PROFILES
].property_name
, DBUS_TYPE_STRING
, names
, n
);
1461 for(unsigned i
= 0; i
< n
; ++i
){
1465 pa_assert_se(dbus_message_iter_close_container(&msg_iter
, &dict_iter
));
1466 pa_assert_se(dbus_connection_send(conn
, reply
, NULL
));
1467 dbus_message_unref(reply
);
1470 void equalizer_handle_seed_filter(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
) {
1471 struct userdata
*u
=(struct userdata
*) _u
;
1480 unsigned x_npoints
,y_npoints
;
1482 dbus_error_init(&error
);
1484 if(!dbus_message_get_args(msg
, &error
,
1485 DBUS_TYPE_ARRAY
, DBUS_TYPE_UINT32
, &xs
, &x_npoints
,
1486 DBUS_TYPE_ARRAY
, DBUS_TYPE_DOUBLE
, &_ys
, &y_npoints
,
1487 DBUS_TYPE_INVALID
)){
1488 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "%s", error
.message
);
1489 dbus_error_free(&error
);
1493 for(size_t i
= 0; i
< x_npoints
; ++i
){
1494 if(xs
[i
] >= u
->fft_size
/ 2 + 1){
1499 if(!is_monotonic(xs
,x_npoints
) || !points_good
){
1500 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "xs must be monotonic and 0<=x<=%ld", u
->fft_size
/ 2);
1501 dbus_error_free(&error
);
1504 }else if(x_npoints
!= y_npoints
|| x_npoints
< 2 || x_npoints
> u
->fft_size
/ 2 +1 ){
1505 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "xs and ys must be the same length and 2<=l<=%ld!", u
->fft_size
/ 2 + 1);
1506 dbus_error_free(&error
);
1508 }else if(xs
[0] != 0 || xs
[x_npoints
-1] != u
->fft_size
/ 2){
1509 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "xs[0] must be 0 and xs[-1]=fft_size/2");
1510 dbus_error_free(&error
);
1514 ys
= pa_xmalloc(x_npoints
* sizeof(float));
1515 for(uint32_t i
= 0; i
< x_npoints
; ++i
){
1516 ys
[i
] = (float) _ys
[i
];
1519 float *H
= u
->Hs
[pa_aupdate_write_begin(u
->a_H
)];
1520 interpolate(H
, u
->fft_size
/ 2 + 1, xs
, ys
, x_npoints
);
1521 fix_filter(H
, u
->fft_size
);
1522 pa_aupdate_write_swap(u
->a_H
);
1523 pa_aupdate_write_end(u
->a_H
);
1526 //Stupid for IO reasons? Add a save signal to dbus instead
1529 pa_dbus_send_empty_reply(conn
, msg
);
1531 DBusMessage
*signal
= NULL
;
1532 pa_assert_se((signal
= dbus_message_new_signal(u
->dbus_path
, EQUALIZER_IFACE
, equalizer_signals
[EQUALIZER_SIGNAL_FILTER_CHANGED
].name
)));
1533 pa_dbus_protocol_send_signal(u
->dbus_protocol
, signal
);
1534 dbus_message_unref(signal
);
1537 void equalizer_handle_get_filter_points(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
) {
1538 struct userdata
*u
=(struct userdata
*) _u
;
1548 dbus_error_init(&error
);
1550 if(!dbus_message_get_args(msg
, &error
,
1551 DBUS_TYPE_ARRAY
, DBUS_TYPE_UINT32
, &xs
, &x_npoints
,
1552 DBUS_TYPE_INVALID
)){
1553 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "%s", error
.message
);
1554 dbus_error_free(&error
);
1558 for(size_t i
= 0; i
< x_npoints
; ++i
){
1559 if(xs
[i
] >= u
->fft_size
/ 2 + 1){
1565 if(x_npoints
> u
->fft_size
/ 2 +1 || !points_good
){
1566 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "xs indices/length must be <= %ld!", u
->fft_size
/ 2 + 1);
1567 dbus_error_free(&error
);
1571 ys
= pa_xmalloc(x_npoints
* sizeof(double));
1572 float *H
= u
->Hs
[pa_aupdate_read_begin(u
->a_H
)];
1573 for(uint32_t i
= 0; i
< x_npoints
; ++i
){
1574 ys
[i
] = H
[xs
[i
]] * u
->fft_size
;
1576 pa_aupdate_read_end(u
->a_H
);
1578 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_DOUBLE
, ys
, x_npoints
);
1582 void equalizer_handle_save_profile(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
) {
1583 struct userdata
*u
=(struct userdata
*) _u
;
1588 dbus_error_init(&error
);
1591 if(!dbus_message_get_args(msg
, &error
,
1592 DBUS_TYPE_STRING
, &name
,
1593 DBUS_TYPE_INVALID
)){
1594 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "%s", error
.message
);
1595 dbus_error_free(&error
);
1598 save_profile(u
,name
);
1599 pa_dbus_send_empty_reply(conn
, msg
);
1601 DBusMessage
*signal
= NULL
;
1602 pa_assert_se((signal
= dbus_message_new_signal(MANAGER_PATH
, MANAGER_IFACE
, manager_signals
[MANAGER_SIGNAL_PROFILES_CHANGED
].name
)));
1603 pa_dbus_protocol_send_signal(u
->dbus_protocol
, signal
);
1604 dbus_message_unref(signal
);
1607 void equalizer_handle_load_profile(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
) {
1608 struct userdata
*u
=(struct userdata
*) _u
;
1614 dbus_error_init(&error
);
1617 if(!dbus_message_get_args(msg
, &error
,
1618 DBUS_TYPE_STRING
, &name
,
1619 DBUS_TYPE_INVALID
)){
1620 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "%s", error
.message
);
1621 dbus_error_free(&error
);
1624 const char *err_msg
=load_profile(u
,name
);
1626 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_FAILED
, "error loading profile %s: %s", name
,err_msg
);
1627 dbus_error_free(&error
);
1630 pa_dbus_send_empty_reply(conn
, msg
);
1632 DBusMessage
*signal
= NULL
;
1633 pa_assert_se((signal
= dbus_message_new_signal(u
->dbus_path
, EQUALIZER_IFACE
, equalizer_signals
[EQUALIZER_SIGNAL_FILTER_CHANGED
].name
)));
1634 pa_dbus_protocol_send_signal(u
->dbus_protocol
, signal
);
1635 dbus_message_unref(signal
);
1638 void equalizer_get_revision(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1640 pa_dbus_send_basic_value_reply(conn
, msg
, DBUS_TYPE_UINT32
, &rev
);
1643 void equalizer_get_n_coefs(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1648 struct userdata
*u
=(struct userdata
*)_u
;
1650 uint32_t n_coefs
=(uint32_t)(u
->fft_size
/ 2 + 1);
1651 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &n_coefs
);
1654 void equalizer_get_sample_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1659 struct userdata
*u
=(struct userdata
*) _u
;
1660 uint32_t rate
=(uint32_t) u
->sink
->sample_spec
.rate
;
1661 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &rate
);
1664 void equalizer_get_filter_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1669 struct userdata
*u
=(struct userdata
*) _u
;
1670 uint32_t fft_size
=(uint32_t) u
->fft_size
;
1671 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &fft_size
);
1674 void get_filter(struct userdata
*u
, double **H_
){
1675 *H_
= pa_xnew0(double, u
->fft_size
/ 2 + 1);
1676 float *H
=u
->Hs
[pa_aupdate_read_begin(u
->a_H
)];
1677 for(size_t i
= 0;i
< u
->fft_size
/ 2 + 1; ++i
){
1678 (*H_
)[i
] = H
[i
] * u
->fft_size
;
1680 pa_aupdate_read_end(u
->a_H
);
1683 void equalizer_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1688 struct userdata
*u
=(struct userdata
*) _u
;
1689 DBusMessage
*reply
= NULL
;
1690 DBusMessageIter msg_iter
, dict_iter
;
1692 uint32_t n_coefs
=(uint32_t)(u
->fft_size
/ 2 + 1);
1693 uint32_t rate
=(uint32_t) u
->sink
->sample_spec
.rate
;
1694 uint32_t fft_size
=(uint32_t) u
->fft_size
;
1696 pa_assert_se((reply
= dbus_message_new_method_return(msg
)));
1697 dbus_message_iter_init_append(reply
, &msg_iter
);
1698 pa_assert_se(dbus_message_iter_open_container(&msg_iter
, DBUS_TYPE_ARRAY
, "{sv}", &dict_iter
));
1700 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, equalizer_handlers
[EQUALIZER_HANDLER_REVISION
].property_name
, DBUS_TYPE_UINT32
, &rev
);
1701 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, equalizer_handlers
[EQUALIZER_HANDLER_SAMPLERATE
].property_name
, DBUS_TYPE_UINT32
, &rate
);
1702 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, equalizer_handlers
[EQUALIZER_HANDLER_FILTERSAMPLERATE
].property_name
, DBUS_TYPE_UINT32
, &fft_size
);
1703 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, equalizer_handlers
[EQUALIZER_HANDLER_N_COEFS
].property_name
, DBUS_TYPE_UINT32
, &n_coefs
);
1706 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, equalizer_handlers
[EQUALIZER_HANDLER_FILTER
].property_name
, DBUS_TYPE_UINT32
, &H
);
1709 pa_assert_se(dbus_message_iter_close_container(&msg_iter
, &dict_iter
));
1710 pa_assert_se(dbus_connection_send(conn
, reply
, NULL
));
1711 dbus_message_unref(reply
);
1714 void equalizer_get_filter(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1719 struct userdata
*u
= (struct userdata
*)_u
;
1720 unsigned n_coefs
= u
->fft_size
/ 2 + 1;
1723 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_DOUBLE
, H_
, n_coefs
);
1727 void set_filter(struct userdata
*u
, double **H_
){
1728 float *H
= u
->Hs
[pa_aupdate_write_begin(u
->a_H
)];
1729 for(size_t i
= 0; i
< u
->fft_size
/ 2 + 1; ++i
){
1730 H
[i
] = (float) (*H_
)[i
];
1732 fix_filter(H
, u
->fft_size
);
1733 pa_aupdate_write_swap(u
->a_H
);
1734 pa_aupdate_write_end(u
->a_H
);
1737 void equalizer_set_filter(DBusConnection
*conn
, DBusMessage
*msg
, void *_u
){
1742 struct userdata
*u
=(struct userdata
*)_u
;
1745 if(pa_dbus_get_fixed_array_set_property_arg(conn
, msg
, DBUS_TYPE_DOUBLE
, &H
, &_n_coefs
)){
1748 if(_n_coefs
!=u
->fft_size
/ 2 + 1){
1749 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "This filter takes exactly %ld coefficients, you gave %d", u
->fft_size
/ 2 + 1, _n_coefs
);
1753 //Stupid for IO reasons? Add a save signal to dbus instead
1756 pa_dbus_send_empty_reply(conn
, msg
);
1758 DBusMessage
*signal
= NULL
;
1759 pa_assert_se((signal
= dbus_message_new_signal(u
->dbus_path
, EQUALIZER_IFACE
, equalizer_signals
[EQUALIZER_SIGNAL_FILTER_CHANGED
].name
)));
1760 pa_dbus_protocol_send_signal(u
->dbus_protocol
, signal
);
1761 dbus_message_unref(signal
);